首先聲明,這不是一個新洞,看過 Homakov 文章(最后附)以及譯文的人想必對這種漏洞有所了解。
但原文寫的太過簡單(沒有說明利用條件、情景和特性),且譯文和我的理解略有偏差,于是就有了這篇文章。
這種漏洞已經存在一段時間了,有沒有被利用過尚不得知,雖然利用條件較苛刻,但是當符合條件的站點被攻擊后, 影響面和影響程度巨大,并且普通用戶不知如何清除, 可導致長期持續攻擊。
2014年底的時候,這種漏洞的利用條件沒有現在苛刻(比如沒有Service-Worker-Allowed
頭),一年過來 W3C 對規范優化了不少(包括安全方面),
相信不久的將來,很快會被標準更新所扼殺了。
彌留之際,讓這個漏洞放點異彩吧。
Service Worker是基于Web Worker的事件驅動的,他們執行的機制都是新開一個線程去處理一些額外的,以前不能直接處理的任務。對于Web Worker,我們可以使用它來進行復雜的計算,因為它并不阻塞瀏覽器主線程的渲染。而Service Worker,我們可以用它來進行本地緩存,相當于一個本地的proxy。說起緩存,我們會想起我們常用的一些緩存技術來緩存我們的靜態資源,但是老的方式是不支持調試的,靈活性不高。使用Service Worker來進行緩存,我們可以用javascript代碼來攔截瀏覽器的http請求,并設置緩存的文件,直接返回,不經過web服務器,然后,做更多你想做的事情。
相信很多人看到這句已經明白了,通過 js 來代理瀏覽器 http 請求,也就是說通過執行 js 代碼來控制瀏覽器的請求, 很容易想到,利用 xss 來修改瀏覽器請求的返回內容。
可怕的是,即便 xss 漏洞被修復了,攻擊仍然持續,并且滲透到攻擊范圍內的每一個 url。
并且,當用戶察覺到攻擊,并且理解這種攻擊,進入chrome后臺(chrome://appcache-internals), 進行手動清除攻擊緩存,攻擊仍未失效!當然了,還是有辦法清除的,且無須用戶手工操作(下文會演示)。
serviceWorker
的官方標準文檔:http://www.w3.org/TR/service-workers/
其操作可以參考:https://github.com/w3c-webmob/ServiceWorkersDemos
首先 serviceWorker 只有在 https 頁面中才可以調用 regist。
而 serviceWorker 需要 Promise 支撐,目前支持的瀏覽器如下:
支持 serviceWorker 的瀏覽器:
firefox 默認關閉 serviceWorker,可以通過 about:config 打開開關:
支持 fetch 方法(抓包)的瀏覽器:
首先在 https 站點中找到一個 Xss,利用 Xss 注冊一個 serviceWorker.registration
實例:
#!js
navigator.serviceWorker.register(url).then(function(registration) {
console.log(registration);
});
注意到有個未知參數 url,這個 url 就是拿來放我們的攻擊代碼(假設我們能上傳一個js到根目錄):
#!js
var url = '//victim.com/evil.js'
有人說這太難了,往根目錄上傳 js 文件不可能,那么可以嘗試在子目錄/任何一個可能的目錄上傳js文件, 或者和 Homakov 一樣,利用 jsonp 接口來代替這個惡意 js 文件。
serviceWorker.register 只支持請求文件返回頭的MIME類型為:text/javascript, application/x-javascript, application/javascript
。
我們知道,jsonp 的 callback 經常是可控的,那么找到一個這樣可以寫代碼的 jsonp 難不難?
Google it !
點擊第一個鏈接:
可以看到,以 taobao.com 為例,第一個 jsonp 接口就存在這樣的弱點:callback 可以寫入任意代碼。
退一步說,只要能輸入 []!() 等幾個符號,就能構造任意代碼了。
以往安全工程師修復 jsonp 接口的 xss 漏洞,都是將頁面的 mime
修改為 application/javascript
,
或者將 callback 的參數中的html符號實體轉義,就覺得杜絕 xss 了,看來以后得換個修法了
若 callback 僅僅代表一個函數名,何不只允許數字、字母和下劃線呢?
#!js
onfetch = function(e) {
e.respondWith(new Response('任意內容',{
headers
...
});
);
}
通過 onfetch 方法攔截 http 請求,并構造返回內容,比如返回:<script>alert(/xss/)</script>
所有在 evil 路徑下的請求的內容都被篡改。
讓我們本地測試還原一遍場景(注意:本地測試不需要 https):
首先打開網站:
打開正常頁面:
這時候點擊被攻擊頁面,此頁面事先被注入了 XSS 腳本:
可以看到,這時候 serviceWorker 已經成功注冊了
刷新頁面,此時返回內容以及被修改了:
這時候再看正常頁面,也被攻擊了:
首頁也是相同的情況:
關閉瀏覽器,再打開,依舊如此:
實際利用中,若弱點JSONP路徑中不存在網站業務,這個漏洞依然能發揮一定價值。
比如:殺死該JSONP路徑以及其子目錄的全部接口,從而導致網站無法正常使用。
從上文可以知道,即便 xss 被修復了或者消失了,攻擊依然生效,那么如何中止攻擊呢?
作為一個普通用戶,首先嘗試打開 chrome://inspect/#service-workers 查看存活:
的確可以看到被用作攻擊的 Worker,點擊 terminate 嘗試中止:
可以看到以及被清理了,但是打開頁面,攻擊仍然存在!
瀏覽器中打開F12
,在console
中輸入:navigator.serviceWorker.
,
可以看到有 getRegistration 和 getRegistrations 這兩種屬性。
查詢文檔:https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/getRegistration
嘗試獲取注冊器,并且調用注銷(由于用到 Promise,需要使用 then 調取結果):
#!js
navigator.serviceWorker.getRegistration()
.then(function(registration) {
registration.unregister();
});
這一次終于清除了。
而對于網站方,如何清除所有攻擊呢?
只要將“清除代碼”部署在一個未受感染的同域的頁面里,當用戶訪問過后,自然就清除了。