新聞中心
前言

成都創(chuàng)新互聯(lián)主要從事成都做網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)陽(yáng)明,10年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18982081108
之前介紹了HTTPS 前端劫持的方案,雖然很有趣,然而現(xiàn)實(shí)卻并不理想。其唯一、也是***的缺陷,就是無(wú)法阻止腳本跳轉(zhuǎn)。若是沒有這個(gè)缺陷,那就非常***了——當(dāng)然也就沒有必要寫這篇文章了。
說到底,還是因?yàn)闊o(wú)法重寫location這個(gè)對(duì)象——它是腳本跳轉(zhuǎn)的唯一渠道。盡管也流傳一些Hack能勉強(qiáng)實(shí)現(xiàn),但終究是不靠譜的。
事實(shí)上,在最近封稿的HTML5 標(biāo)準(zhǔn)里,已非常明確了location 的地位——Unforgeable。
這是個(gè)不幸的消息。不過也是件好事,讓我們徹底打消各種偏門邪道的念頭,尋求一條全新的出路。
替換明文URL
上回也提到,可以參考 SSLStrip 那樣,把腳本里的 HTTPS URL 全都替換成 HTTP 版本,即可滿足部分場(chǎng)合。
當(dāng)然,缺陷也是顯而易見的。只要 URL 不是以明文出現(xiàn) —— 例如通過字符串拼接而成,那就完全無(wú)法識(shí)別了,最終還是無(wú)法避免跳轉(zhuǎn)到 HTTPS 頁(yè)面上。
這種情況并不少見,所以我們需要更解決方案。
替換location
盡管我們無(wú)法重寫 location,但要山寨一個(gè)和 location 功能一樣的玩意,還是非常容易的。我們只需定義幾個(gè) getter 和 setter,即可模擬出一個(gè)功能完全相同的location2。但如何將原先的 location 映射過來呢?
這時(shí),后端的作用就發(fā)揮出來了。類似替換 HTTPS URL,這次我們只關(guān)注腳本里的 location 字符,把它們都改成 location2 —— 于是所有和地址欄相關(guān)的讀寫,都將落到我們的代理上面。之后能做什么,不用說大家也都明白吧。
代理所有的 setter:如果跳轉(zhuǎn)到 HTTPS 就將其攔下,然后降級(jí)到 HTTP 版本上。
代理所有的 getter:如果當(dāng)前處于降級(jí)的頁(yè)面,我們將返回的路徑都還原 HTTPS 字符,即可騙過協(xié)議判斷腳本,讓那些自檢功能徹底失效!
相比之前的 URL 替換,這個(gè)方案***太多 —— URL 是動(dòng)態(tài)創(chuàng)建的非常普遍,但 location 不是明文出現(xiàn)的,及其罕見。
除非腳本是加密過的,否則即使用 Uglify 那樣的壓縮工具,也不會(huì)把全局變量給混淆。至于人為刻意去轉(zhuǎn)義它,更是無(wú)稽之談了。
if (window['loc\ation'].protocol != 'https:') {
// ...
}
到此,我們的目標(biāo)已經(jīng)明確了:
前端:實(shí)現(xiàn)一個(gè) location 代理。
后端:將腳本里出現(xiàn)的 location 替換成代理變量名。
處理外鏈腳本
雖然替換頁(yè)面腳本的內(nèi)容并不困難,但對(duì)于外鏈腳本,那就不容樂觀了。
現(xiàn)實(shí)中,不少頁(yè)面外鏈了 HTTPS 絕對(duì)路徑 的腳本。這時(shí),我們的中間人就無(wú)能為力了。為了避免這種情況,我們?nèi)孕杼鎿Q頁(yè)面里的 HTTPS URL,讓中間人能掌控更多的資源。
要替換 URL 倒也不難,一個(gè)簡(jiǎn)單的正則就能實(shí)現(xiàn) —— 但既然使用正則,我們面對(duì)的只能是字符串了。
然而事實(shí)上,收到的都是最原始的二進(jìn)制數(shù)據(jù),甚至未必都是 UTF-8 的。在上一篇文章里,我們?yōu)榱撕?jiǎn)單,直接使用二進(jìn)制的方式注入。但在如今,這個(gè)方法顯然不可行了。
使用二進(jìn)制,不僅難以控制,而且很不嚴(yán)謹(jǐn)。我們很難得知匹配到的是獨(dú)立的字符,還是一個(gè)寬字符的部分字節(jié)。因此,我們還是得用傳統(tǒng)可靠的方式來處理字符串。
處理字集編碼
我們得借助字集轉(zhuǎn)換庫(kù),例如大名鼎鼎的 iconv,來協(xié)助完成這件事:
首先將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成 UTF-8 字符串
有了標(biāo)準(zhǔn)的字符串,我們的正則即可順利執(zhí)行了
將處理完的字符串,重新?lián)Q回先前的編碼
盡管這一來一回得折騰兩次,性能又得耗費(fèi)不少,但這仍是必須的。
事實(shí)上,這個(gè)過程也不是想象的那么順利。有相當(dāng)多的服務(wù)器,并沒有在返回的 Content-Type 里指定編碼字集,于是我們只能嘗試從頁(yè)面的 中獲取。
但這個(gè)標(biāo)簽兼容諸多規(guī)范,例如過去的:
以及如今流行的:
盡管通過正則很容易獲取,但用正則的前提還是得先有字符串,于是我們陷入了僵局。
不過好在標(biāo)簽、屬性、字集名,基本都是純 ASCII 字符,所以可先將二進(jìn)制轉(zhuǎn)成默認(rèn)的 UTF-8 字符串,從中取出字集信息,然后再進(jìn)行轉(zhuǎn)碼。
處理數(shù)據(jù)分塊
得益于豐富的第三方擴(kuò)展,上述問題都不難解決。
然而,之前提到過『前端劫持』的一個(gè)巨大優(yōu)勢(shì) —— 無(wú)需處理所有數(shù)據(jù),只需在***個(gè) chunk 里注入代碼即可。但現(xiàn)在,這項(xiàng)優(yōu)勢(shì)面臨著嚴(yán)峻的考驗(yàn)。
我們要替換頁(yè)面里的 HTTPS 資源、location 變量等等,它們會(huì)出現(xiàn)在頁(yè)面的各個(gè)位置。如果我們對(duì)每個(gè) chunk 進(jìn)行單獨(dú)過濾、轉(zhuǎn)發(fā),這樣會(huì)有問題嗎?
現(xiàn)實(shí)中,未必都是這樣理想的 —— 總會(huì)有那么一定的幾率,替換的關(guān)鍵字正好跨越兩個(gè) chunk:
這時(shí)候,殘缺的首尾都無(wú)法匹配到,于是就會(huì)出現(xiàn)遺漏。關(guān)鍵字越長(zhǎng),出現(xiàn)的幾率也就越大。對(duì)于 URL 這樣長(zhǎng)的字符串來說,這是一個(gè)潛在的隱患。
要***解決這個(gè)問題,是比較麻煩的。不過有個(gè)簡(jiǎn)單的辦法:我們可以扣留下 chunk 末尾部分字符,拼接到下個(gè) chunk 的之前,從而降低遺漏的可能。
當(dāng)然,如果不考慮用戶體驗(yàn)的話,還是收集完所有數(shù)據(jù),***一次性處理,最省事了。
事實(shí)上還有更好的方案:中間人開啟一個(gè)緩沖區(qū),將收到數(shù)據(jù)暫時(shí)緩存其中。當(dāng)數(shù)據(jù)積累到一定量、或者超過多久沒有數(shù)據(jù)時(shí),才開始批量處理緩存隊(duì)列。
這樣就可以避免 頻繁的 chunk 上下文處理,同時(shí)也 不會(huì)長(zhǎng)時(shí)間阻塞用戶的響應(yīng)時(shí)間,自然是兩全其美的。
這是不是有點(diǎn)類似 TCP nagle 的味道呢。
前端location代理
講完了后端的相關(guān)細(xì)節(jié),我們繼續(xù)回到前端的話題上。
實(shí)現(xiàn)一個(gè) location 的代理很簡(jiǎn)單,不過值得留意的細(xì)節(jié)倒是不少:
location 不僅存在于 window,其實(shí) document 里也有個(gè)相同的。
location 對(duì)象本身也是可以被賦值的,效果等同于 location.href。([PutForwards=href, ...]已經(jīng)很好的解釋了)
同理,location 的 toString 返回的也是 href 屬性。
如果帶有 location2 的腳本被緩存住了,那么用戶在沒有劫持的頁(yè)面里,也許就會(huì)報(bào)錯(cuò)。所以還得留一條兼容的后路。
......
只要考慮充分,實(shí)現(xiàn)一個(gè) location 的切面還算是比較容易的。
動(dòng)態(tài)腳本劫持
前面談到替換頁(yè)面的 HTTPS URL,以確保外鏈腳本明文傳輸。
然而現(xiàn)實(shí)中,并非所有腳本都是靜態(tài)的。如今這個(gè)腳本泛濫的時(shí)代,動(dòng)態(tài)加載模塊是很常見的事。如果引入的是一個(gè) HTTPS 的腳本,那么我們的中間人又無(wú)從下手了。
不過值得慶幸的是,模塊攔截不像 location 那樣無(wú)法實(shí)現(xiàn)?,F(xiàn)實(shí)中,有非常多的方法可以攔截動(dòng)態(tài)模塊。在之前寫的《XSS 前端防火墻 —— 可疑模塊攔截》 一文里,已經(jīng)詳細(xì)討論過各種方法和細(xì)節(jié),這里正好派上用場(chǎng)。
事實(shí)上,除了腳本外,框架頁(yè)同樣也存在這個(gè)問題。上一篇文章里,我們采用 CSP 來阻擋 HTTPS 的框架頁(yè)。但那僅僅是屏蔽,并不是真正意義的攔截。只有加上如今這套鉤子系統(tǒng),才算一個(gè)完善的攔截系統(tǒng)。
演示
說了那么多,真正的核心無(wú)非就是改變腳本里的 location 變量而已,其他的一切都只是為了輔助它。
下面我們找?guī)讉€(gè)之前無(wú)法成功的網(wǎng)站,試驗(yàn)下這個(gè)加強(qiáng)版的劫持工具。
上一篇文章里提到京東登錄,就是通過腳本跳轉(zhuǎn)的。我們首先就拿它測(cè)試:
當(dāng)流量經(jīng)過中間人代理,頁(yè)面和腳本里的 location 都變成了我們的變量名。于是之后和地址欄相關(guān)的一切,盡在我們的掌控之中了:
注意地址欄里有一個(gè) zh_cn 的標(biāo)記,那正是 URL 向下轉(zhuǎn)型后的識(shí)別暗號(hào)。
通過 location2 獲取到的一切屬性,看起來就像在 HTTPS 頁(yè)面上一模一樣。即使腳本里有自檢功能,也會(huì)被我們的虛擬環(huán)境所欺騙。
點(diǎn)擊登錄,自然是成功的。
畢竟,HTTPS 和 HTTP 只是傳輸上的差異。在應(yīng)用層上,頁(yè)面是無(wú)法知曉的 —— 除了詢問腳本的 location,但它已被我們劫持了。
除了京東的腳本跳轉(zhuǎn),財(cái)付通網(wǎng)站則是通過非主流的 進(jìn)行的。
好在我們對(duì)頁(yè)面里的 HTTPS URL 都替換了,所以仍然能夠跳轉(zhuǎn)到降級(jí)后的頁(yè)面:
值得注意的是,如果是從 QQ 圖標(biāo)里點(diǎn)進(jìn)來的,那么頁(yè)面就直接進(jìn)入 HTTPS 版本,就不會(huì)被劫持了。但從第三方過來那就聽天由命了。
由于一般開發(fā)人員的思維,是不可能轉(zhuǎn)義 location 這個(gè)變量的。因此這套方案幾乎可以通殺所有的安全站點(diǎn)。
當(dāng)然,外國(guó)的網(wǎng)站也是一樣的。只要之前沒有被 HSTS 所緩存,劫持依舊輕松自如。
......
所以,只要發(fā)揮無(wú)盡的想象,實(shí)現(xiàn)一個(gè)工程化的通用劫持方案,依然是可行的。
防范措施
如果你是仔細(xì)看完本文的話,應(yīng)該早就想到如何應(yīng)對(duì)了。
事實(shí)上,由于 JS 具有超強(qiáng)的靈活性,幾乎無(wú)法從靜態(tài)源碼推測(cè)運(yùn)行時(shí)的行為。
因此,只要將涉及 location 相關(guān)操作,進(jìn)行簡(jiǎn)單的轉(zhuǎn)義混淆,就能躲過中間人的劫持了。畢竟,要在劫持流量的同時(shí),還要對(duì)腳本進(jìn)行語(yǔ)法分析,這個(gè)代價(jià)不免有點(diǎn)大了。
原文地址:http://www.cnblogs.com/index-html/p/sslstrip-plus.html
分享標(biāo)題:SSLStrip終極版——location瞞天過海
文章路徑:http://m.fisionsoft.com.cn/article/codsgei.html


咨詢
建站咨詢
