taibeihacker
Moderator
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。
當然,並非任何情況都能收回的。如果跳轉到了不同源的頁面,那顯然是無能為力了—— 不過,就此而放棄它嗎?回答是:決不妥協!
儘管頁面已經和我們分道揚鑣了,但所在的窗體仍然被我們掌控。我們可以跳轉、關閉它,甚至還有可能出現奇蹟:只要页面跳转回我们的站点,又可被我们所收复!
當有新成員加入時,通知給大家,記錄在各自的頁面裡。
這樣即使其中一個頁面意外關閉了,也不會丟失重要的信息—— 信息已被分佈儲存在各個頁面裡了。
因此,頁面開的越多,相互聯結就越牢固。
所以,把超鏈接都變成新頁面中打開,還是有很大的優勢的。
如果只剩最後一個頁面,那麼一旦刷新之後就沒人來拯救了,於是就會消亡。
因此,遇到不同源的頁面,可以嘗試降低自身的域,再次發起操作,或許就能成功注入了。
相比超鏈接,表單顯得棘手一些。我們不僅得打開一個新頁面,還要把表單裡的數據也提交上去。如果把整個表單的元素克隆到新頁面提交,一些數據又會丟失。
不過,仔細研究一下表單元素,會發現有一個非常簡單的方法:原來window.open 第二個參數可以賦予新窗口一個name,然後將name 賦予表單的target 屬性,即可在我們創建的新窗口裡提交。這樣就可以把XSS 注入進去了。
也許,XSS 位於頁面中某個小框架。如果只局限於自身頁面,那麼是毫無發展空間的。因此得跳出圈子,向更廣闊的parent 頁面注入。
類似的,如果主頁面僅僅是個外殼,實際內容運行在某個框架裡,那麼得注入到子框架中,才能獲取更有意義的信息。
同樣,我們還可以將上下注入結合,即可讓XSS 從某個框架頁裡破殼而出,感染到所有的框架頁裡。
不過好在除IE 外的其他主流瀏覽器,都能很好的運行。下面分享一個Demo,其中實現了上述部分功能:
當我們順著超鏈接往前點,一旦進入有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,其中實現了上述部分功能:
當我們順著超鏈接往前點,一旦進入有XSS 的頁面,先前的父頁面都遭到感染。更嚴重的是,被感染的頁面打開的子頁面,也都無一倖免。即使刷新,也會被其他頁面監控到,從而立即恢復。
正如幽靈鬼魂一般揮之不去。