XSS攻擊:獲取瀏覽器記住的明文密碼

0x01. XSS獲取明文密碼的多種方式
這篇文章是想深入描述下瀏覽器記住用戶密碼這種機制帶來的安全問題與實現上的一些差異性。黑客們如何通過技巧獲取到瀏覽器保存的密碼,明文。
先回到XSS本身上,XSS獲取明文密碼的方式有哪些?
1.通過DHTML釣魚方式
比如document.write出一個登陸頁面,或一個登錄框。也就是目標網站的登錄方式是怎樣的,就通過DOM模擬出怎樣的。用戶很難區分同域內的釣魚,如果再次輸入密碼登錄時就悲劇了。
2.通過JavaScript hook住密碼框
滿足某個事件(如onsubmit/onblur/onchange等)就記錄之。
3.通過JavaScript實現鍵盤記錄器的功能
監聽用戶在表單裡的擊鍵事件,記錄擊鍵的值,有時候這個效果會非常不錯。
上面這三個方法都是大家在用的,效果各有千秋,這次我要提一種新的方式:通過利用瀏覽器保存密碼這個機制來達到獲取明文密碼的目的,效果顯得更加直接。
0x02. 瀏覽器記住密碼的機制
現在回到瀏覽器的這個機制上,最早是哪個瀏覽器實現的,我懶得去考證了,可能是IE。在用戶登錄的時候,瀏覽器會提示保存密碼,可以在下面這個地址在線查看我測試的8個瀏覽器截圖:
http://evilcos.me/lab/xss_pwd/
註:這也是本文的demo地址,測試的瀏覽器有:
Chrome(v 16.0.912.75 m)
IE9
IE8
Firefox(v 9)
Opera(v 11.60)
Safari(v 5.1.2)
Maxthon(v 3.2.2.1000)
Sogou(v 3.0.0.3000)
保存密碼是為了提高用戶體驗,省去了每次登錄需要輸密碼的麻煩,在這個機制之前經常是通過身份認證的本地Cookie來實現的,也許是因為並不是所 有網站都採用持久化Cookie,瀏覽器才開始選擇了這樣的方式。而且現代瀏覽器大多有一個機制:云同步,除了書籤、個人偏好外,還可以同步瀏覽器記住的 密碼,使得用戶在任意地方都可以同步自己的「習慣」。有的身份認證Cookie是綁定IP的,這樣的話同步Cookie就不好使了。簡而言之吧,密碼這東 西就是方便,可也太濫用了,濫用有風險,而且還來了個云同步,黑客興奮了。
在瀏覽器記住密碼機制之前大家應該都知道還有一個很火熱的機制:表單自動填充!曾經出現的安全風險是:由於這個自動填充的值是跨域共享的,攻擊者可 以在自 己的域放一個頁面,用戶的瀏覽器訪問後,會自動填充這個頁面的表單(比如Email、家庭地址、手機號等等,如果用戶的瀏覽器記住過這些值的話),然後這 個頁面的JavaScript就可以獲取到這些值了。這些值還好,攻擊者並不一定很喜歡,可是明文密碼就不一樣了。
記住密碼機制需要遵循同源策略,但是如果有XSS就可以忽略這個同源策略,注入JavaScript去得到這個明文密碼:P
下面我以Chrome為例深入說明說明,攻擊者通過這個機制是如何得到你的明文密碼的。Opera與IE的機制相對來說是最安全的,而搜狗瀏覽器在這方面的安全性最差。
0x03. 獲得Chrome記住的密碼
先來看Chrome,demo地址:http://evilcos.me/lab/xss_pwd/。可以輸入admin/1234567,然後LOGIN試試。瀏覽器彈出保存密碼提示時,選擇保存。重新載入這個demo地址,可以看到瀏覽器已經自動填充了密碼。點擊按鈕「see ur pwd」會彈出你輸入的密碼明文。
實際上你查看頁面源代碼是看不到密碼的,這個密碼是瀏覽器判斷頁面加載後,發現表單中有密碼項,就自動填充最近一次記錄的用戶名與密碼,就像(或者 說就 是)一次DOM操作,動態填充。既然是DOM操作,那麼在這之後我們控制JavaScript也來一次DOM操作,這次是讀,將密碼項裡的value值讀 出來,是不是就得到了明文密碼?對……是這樣!
知道這個過程後,邪惡的想法誕生了……
這個機制遵循同源策略,那麼如果在一個域內,任意頁面存在XSS,就應該可以通過DOM動態創建一個包含一模一樣的用戶名與密碼表單項的表單出來,然後等待瀏覽器自動填充密碼後,再通過DOM操縱得到密碼項裡的值。
開始實驗!
這個頁面http://evilcos.me/lab/xss_pwd/的表單是這樣:
<form method="post" action=".">
 <label for="username">USER: </label><input id="username" name="username" type="text" class="text" value="" />
 <label for="password">PASS: </label><input id="password" name="password" type="password" class="text" value="" />
 <input type="submit" class="submit" value="LOGIN" />
 <input type="hidden" name="next" value="" />
</form>
瀏覽器是如何記住這個表單的,以確保唯一性?有幾個關鍵值(不同瀏覽器有差異,不過影響不大):
1.為了遵循同源策略,需要域名:evilcos.me
2.需要一個<form>標籤
3.需要id或name為username的用戶名<input>表單項
4.需要id或name為password的密碼<input>表單項
如果是這樣,攻擊者發現同域內XSS後,就要開始構造一段payload,這個payload用於自動創建出這樣的表單,這個表單瀏覽器要能夠認識 (認為 是之前記住密碼的那個表單:P),並且必須在瀏覽器開始自動填充密碼之前出來(否則得不到填充值),最後必須在瀏覽器填充完密碼後開始獲取表單項的值(否 則獲取到的值是空的)。
條件好像很苛刻,哪個步驟時間把握不好,攻擊就失敗了。針對這個場景我構造了一個payload,如下:
function create_form(user) { /*獲取明文密碼*/
 var f = document.createElement("form");
 document.getElementsByTagName("body")[0].appendChild(f);
 var e1 = document.createElement("input");
 e1.type = "text";
 e1.name = e1.id = "username";
 e1.value = user;
 f.appendChild(e1);
 var e = document.createElement("input");
 e.name = e.type = e.id = "password";
 f.appendChild(e);
 setTimeout(function () {
  alert("i can see ur pwd: " + document.getElementById("password").value);
 }, 3000); // 時間競爭
}

create_form('');
也可以查看http://evilcos.me/lab/xss_pwd/xssme.html的 代碼,create_form函數的執行優先於整個document文檔的完全解析,這時會自動創建一個登錄表單(和之前記住密碼的表單關鍵部分是一樣 的,這就足夠了),然後等待3000毫秒,待整個document文檔解析結束(此時瀏覽器已經完成了密碼填充),最後獲取密碼表單項裡的值,成功!
3000毫秒不靠譜就來個for循環直到獲取到密碼值才退出。
0x04. 插一個題外點:時間競爭
這個話題很大,就是誰先誰後的問題,不僅和瀏覽器解析處理整個DOM樹的順序有關係,也和我們要達到的目的有關,比如瀏覽器解析順序的一個經典例子:
<script src='http://remote/x.js'></script>
<body></body>
是先解析完遠程的js腳本,還是先解析<body>標籤?
如果這樣呢?
<script id='rfi'></script>
<script>
document.getElementById('rfi').src = 'http://remote/x.js';
</script>
<body></body>
我們最好對「時間競爭」心裡有數,搞清楚瀏覽器解析的機制,這樣我們的payload才能達到我們的目的。
0x05. 各瀏覽器的差異
我已經習慣差異了,而且喜歡差異,因為這樣很可能會帶來一些安全問題,不過前端工程師們就不喜歡了:&,下面我只講關鍵的差異,那些小的,大家自己試驗,自己發現。
1 Safari瀏覽器
只有Safari默認是關閉這個機制的。如果開啟後,效果和chrome一樣,非常好用!
2.Opera瀏覽器
Opera好像很安全,記住密碼後,瀏覽器並不會自動填充密碼,而是要用戶自己點擊地址欄左邊的鑰匙圖標,才會開始填充並登錄。
3.IE8/9瀏覽器
IE8/9及部分這個內核的瀏覽器(比如遨遊的IE模式)很聰明,將每個登錄表單綁定到所在的頁面上(下面簡稱這個頁面為綁定頁面),由於綁定頁面地址是唯一的,同域內其他頁面就無法通過生成一個一模一樣的表單來獲取密碼了。
如果就這樣還是不安全:P,因為XSS可以動態iframe進這個綁定頁面,然後注入JS進行任意DOM操作,同樣非常容易獲取到密碼表單項的 值,IE估 計是考慮到了這個,通過iframe調用綁定頁面也無效。而且IE的機制還遠沒這樣簡單,即使在綁定頁面內我也沒成功得到密碼,因為IE默認並不填充密 碼,只有輸入正確用戶名後,並觸發類似onblur事件,這個密碼表單項才會填充進對應用戶名的密碼。這個過程我本想通過DOM來模擬進行的,但是沒有成 功。感興趣的同學可以試試。
4.其他瀏覽器
其他瀏覽器(除了搜狗瀏覽器)都和Chrome差不多了,大多是因為webkit內核。下面單獨說說搜狗瀏覽器吧。
0x06. 搜狗瀏覽器「記住密碼機制」的安全缺陷
搜狗瀏覽器在實現這個機制估計是下了一些苦工了,雙核模式下都很好兼容,不過安全方面的實現存在一些問題,並沒嚴格遵循同源策略。在我的測試中發現,搜狗沒區分好不同端口及不同子域的同源問題。
比如在www.foo.com域下記住的密碼,在a.foo.com與www.foo.com:8080域中都可以讀取到。
還有一個有意思的,我們的payload甚至僅創建一個password表單項(<form>都可以不需要)就可以得到明文密碼。看來搜狗瀏覽器在實現這個機制有偷工減料的嫌疑啊,用戶體驗雖然不錯。
0x07. 如何防禦
從三個方面進行:瀏覽器、網站、用戶。
1.瀏覽器防禦
IE的機制相對來說很不錯了,其他瀏覽器可以借鑑,雖然這樣會影響一些用戶體驗,我想為了更安全也值得了吧,需要特別注意的,IE這個機制有好幾個關鍵點,不要到時候依葫蘆畫瓢,學不好讓人笑了:P
2.網站防禦
通常給表單的<form>標籤設置autocomplete="off"即可,不過不是所有瀏覽器都兼容,我發現搜狗與遨遊瀏覽器不買這個帳。或者不要<form>標籤了,通過JS自提交登錄。新浪微博採用了這兩種方式,其他網站可以學學。
3.用戶防禦
意識為先吧,瀏覽器記住你的密碼需謹慎,沒必要的就不用記了。
0x08.總結
到這還沒結束,大家可以試試給表單多增加一個項或者少一個項,不同瀏覽器還是存在很多差異,這個大家自己找吧。
這個安全問題我很早就發現,也公開過,不過沒引起足夠重視:P,如果一個SNS類的網站中傳播XSS蠕蟲,帶上這樣的payload,不知道能獲取多少明 文密碼……或者在定點滲透過程中,如郵箱XSS滲透,帶上這樣的payload,一定概率說不定可以拿到明文密碼。怎麼個危害,就看怎麼個場景,怎麼個利 用。

留言

這個網誌中的熱門文章

c語言-關於#define用法

CMD常用網管指令

PHP 與 JavaScript 之間傳值利用 json