【XSS】延長 XSS 生命期

XSS 的本質仍是一段腳本。和其他文檔元素一樣,頁面關了一切都銷燬。除非能將腳本蔓延到頁面以外的地方,那樣才能獲得更長的生命力。

慶幸的是,從 DOM 誕生的那一天起,就已爲我們準備了這個特殊的功能,讓腳本擁有突破當前頁面的能力。

下面開始我們的續命黑魔法。

反向注入

一個不合理的標準,往往會埋下各種隱患。

瀏覽器提供了一個 opener 屬性,供彈出的窗口訪問來源頁。但該規範設計的並不合理,導致通過超鏈接彈出的頁面,也能使用 opener。

但按理來說,只有通過腳本彈出的子頁面,才能擁有 opener 屬性,這樣可以相互訪問和操作。

然而事實上,通過超鏈接點開的頁面居然也有!這爲 XSS 打開了一扇大門 —— XSS 不僅可以操控當前頁面,甚至還能傳染給同源的父頁面。

XSS 一旦感染到父頁面裏,戰鬥力就大幅提升了。

可以想象,只要看了一個帶有 XSS 的帖子,即使立即關了,那麼帖子列表頁也會遭到感染。

更有趣的是,opener 這個屬性不受同源策略限制。即使父頁面不同源,但父頁面的 opener 仍然可以訪問。

我們可以順着 opener.opener.opener... 一直往上試探,只要是和當前頁面同源的,仍然能夠進行操控 —— 儘管中間隔着其他不同源的頁面。

網站的主頁面顯然比詳細頁更受用戶的信任,停留的時間也會更長,因此攻擊力可成倍的增加。

正向注入

如果說反向注入是苟且偷生的話,那麼正向注入就是當家做主翻身的機會了。

儘管我們能夠控制父頁面,但從父頁面點開的網頁仍然不受操控。如果具有控制子頁面的能力,那就更完美了。

不幸的是,我們無法控制超鏈接打開的新頁面。唯一能夠操控的新頁面,那就是 window.open 的彈框頁。幸運的是,在絕大多數瀏覽器上,它們看起來的效果是一樣的。

因此,我們可以在用戶的點擊瞬間,屏蔽掉默認的超鏈接行爲,用彈框頁取而代之,即可把 XSS 注入到 window.open 返回的新頁面裏了。

類似的,通過子頁面遞歸打開的新頁面,同樣也無法逃脫。於是子子孫孫盡在我們的掌控之中。

反向注入,讓我們佔據已有的地盤;正向注入,把我們的勢力擴大蔓延出去。兩者結合,即可佔據半壁江山了。

值得注意的是,正向注入中有個細節問題。並非所有的超鏈接都是彈出型的(_blank),也有不少是在當前頁面跳轉的。若是想劫持的狠點,可以忽略這個問題;如果不想被細心的用戶發現,那麼可以判斷下當前超鏈接以及<base>的 target 屬性,決定是否劫持。

頁面監督

上面提到,如果是在當前頁面裏跳轉,那麼還能繼續感染嗎?或者說,某個頁面刷新之後,是否就丟失了?

答案是肯定的。如果我們不採取一些措施,任憑佔據的地盤不斷丟失,那麼我們的勢力範圍就會越來越小,直到消亡。

相比進攻,防守則更爲困難。我們不知何時會失去,因此必須定時去檢查。

一旦發現對方已擺脫我們的控制,那麼必須立即重新注入,以恢復我們的勢力。

對於新頁面的 XSS 來說,當然是注入的越早越好。越前面擁有越高的優先級,甚至可以攔截頁面的正常業務功能。

爲了能儘快獲知頁面刷新、跳轉等行爲,我們還可跟蹤 unload 事件,在頁面即將丟失的瞬間,將消息通知出去,讓對方儘快來拯救自己。

這樣,就不必等待定時器了,可以最快的速度恢復。甚至能趕在頁面的第一個腳本之前,運行我們的 XSS。

當然,並非任何情況都能收回的。如果跳轉到了不同源的頁面,那顯然是無能爲力了 —— 不過,就此而放棄它嗎?回答是:決不妥協!

儘管頁面已經和我們分道揚鑣了,但所在的窗體仍然被我們掌控。我們可以跳轉、關閉它,甚至還有可能出現奇蹟:只要頁面跳轉回我們的站點,又可被我們所收復!

互相聯結

不難發現,只要還有一個頁面存在,就有可能收回曾經被佔領的地盤。因此,我們要將可控的頁面都聯結起來,讓每個頁面都知曉並監督所有成員。

當有新成員加入時,通知給大家,記錄在各自的頁面裏。

這樣即使其中一個頁面意外關閉了,也不會丟失重要的信息 —— 信息已被分佈儲存在各個頁面裏了。

因此,頁面開的越多,相互聯結就越牢固。

所以,把超鏈接都變成新頁面中打開,還是有很大的優勢的。

如果只剩最後一個頁面,那麼一旦刷新之後就沒人來拯救了,於是就會消亡。

降域嘗試

一些網站爲了方便通信,將 document.domain 降到根域。例如支付寶網站的絕大部分頁面,都是 alipay.com。這樣原本不同源的子站,這時也能夠相互操控了。

因此,遇到不同源的頁面,可以嘗試降低自身的域,再次發起操作,或許就能成功注入了。

表單劫持

之前說到正向注入,是通過劫持超鏈接點擊實現的。事實上,除了超鏈接外,還有個進入新頁面的方式,那就是表單提交。

相比超鏈接,表單顯得棘手一些。我們不僅得打開一個新頁面,還要把表單裏的數據也提交上去。如果把整個表單的元素克隆到新頁面提交,一些數據又會丟失。

不過,仔細研究一下表單元素,會發現有一個非常簡單的方法:原來 window.open 第二個參數可以賦予新窗口一個 name,然後將 name 賦予表單的 target 屬性,即可在我們創建的新窗口裏提交。這樣就可以把 XSS 注入進去了。

框架注入

不同頁面之間可以正反注入。同個頁面中,可能存在多個框架頁,因此還可以嘗試框架頁之間的上下注入。

也許,XSS 位於頁面中某個小框架。如果只侷限於自身頁面,那麼是毫無發展空間的。因此得跳出圈子,向更廣闊的 parent 頁面注入。

類似的,如果主頁面僅僅是個外殼,實際內容運行在某個框架裏,那麼得注入到子框架中,才能獲取更有意義的信息。

同樣,我們還可以將上下注入結合,即可讓 XSS 從某個框架頁裏破殼而出,感染到所有的框架頁裏。

後記

儘管這些特徵從 DOM 誕生起就已存在了,不過要寫出一個完善的腳本並不容易。直到如今的 IE 11,不同窗體間的操作,仍有各種奇怪問題,更不用說那些非主流 IE 了。

不過好在除 IE 外的其他主流瀏覽器,都能很好的運行。下面分享一個 Demo,其中實現了上述部分功能:

https://www.etherdream.com/FunnyScript/XSSGhost/

當我們順着超鏈接往前點,一旦進入有 XSS 的頁面,先前的父頁面都遭到感染。更嚴重的是,被感染的頁面打開的子頁面,也都無一倖免。即使刷新,也會被其他頁面監控到,從而立即恢復。

正如幽靈鬼魂一般揮之不去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章