XSS典型的瀏覽器端漏洞,由於用戶的輸入未經轉義直接輸出到頁面中,惡意程式碼在用戶的瀏覽器中被解析,從而造成危害。傳統的反射型XSS可以通過判斷頁面源碼是否含有特定字串來檢測。但由於Web 2.0的快速發展互動越來越複雜,DOM-XSS也層出不窮,導致傳統的檢測方案的漏報率很高。本文主要介紹了如何利用PhantomJS + Python完成動態檢測。
0x01 PhantomJS
既然是動態檢測,那麼就需要一個瀏覽器,但普通的瀏覽器在渲染頁面上花費了太多的資源和時間,並不適用。怎麼辦?當然開源世界早有解決方案:PhantomJS、PyQt、CEF等等。對比了一下上手難易程度、檔案豐富程度等,我選擇了PhantomJS進行開發。
PhantomJS是無介面的Webkit解析器,提供了JavaScript API。由於去除了視覺化介面,速度比一般Webkit瀏覽器要快很多。同時提供了很多監控和觸發介面,可以方便的操作頁面DOM節點,類比用戶操作等。
0x02漏洞判別標準
XSS漏洞,說到底還是用戶輸入被當成頁面程式碼解析了。解析的結果,可能是執行了JS程式碼,也可能是在頁面中創建/修改了某個DOM節點。所以我們將Payload分為兩類:
- 第一類,執行了指定的JS程式碼(alert(1))
alert(1)
- 第二類,創建了新的DOM節點(<xsstest></xsstest>)。
<xsstest></xsstest>
根據這兩種Payload,自然而然的推出了漏洞判別標準:
- 頁面彈窗(在PhantomJS中重載window.alert)
window.alert
- 新節點(解析玩頁面後,判斷document.getElementsByTagName('xsstest')是否為空)。
document.getElementsByTagName('xsstest')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
page.onAlert = function(message){
if(message == xss_mark){
xss_exists = 1;
ret =“Success,xss exists”;
phantom_exit(ret);
}
console.log('Alert: ' + message);
return true;
};
function check_dom_xss_vul(){
return document.getElementsByTagName(dom_xss_mark).length;
}
為了驗證檢測程式碼,編寫一個簡單存在XSS漏洞的頁面。
1
2
3
<?php
echo $_GET['test'];
?>
經測試,訪問http://127.0.0.1:8000/xss.php?test=<img src=1 onerror=alert(1)>,我們的檢測程式碼成功檢測到了彈窗,並返回了正確的結果。但是,如果是下麵這種情況呢?
http://127.0.0.1:8000/xss.php?test=<img src=1 onerror=alert(1)>
1
2
3
4
5
<?php
$click = $_GET['test'];
echo“<div onclick=$click></div>”;
?>
0x03執行事件程式碼
很明顯,我們需要執行onclick中的程式碼,才能檢測到漏洞。首先我們想到的是觸發事件,僅僅是觸發click事件,很簡單,javascript本身就提供了click事件:document.getElementsByTagName('div')[0].click()。但是javascript也就僅僅提供了click事件的觸發函數而已。
onclick
document.getElementsByTagName('div')[0].click()
但既然程式碼直接輸出在了onclick/onmouseover之類的内容裏,我們遍歷所有節點的内容,針對onxxxxx的屬性值,直接調用eval方法,執行對應的程式碼就可以了。
onclick/onmouseover
1
2
3
4
5
6
7
8
9
10
11
12
var nodes = document.all;
for(var i=0;i<nodes.length;i++){
var attrs = nodes[i].attributes;
for(var j=0;j<attrs.length;j++){
attr_name = attrs[j].nodeName;
attr_value = attrs[j].nodeValue;
if(attr_name.substr(0,2)==“on”){
console.log(attrs[j].nodeName + ':' + attr_value);
eval(attr_value);
}
}
}
訪問http://127.0.0.1:8000/xss.php?test=alert(1)成功執行程式碼,但新的問題很快出現:並不是所有的JS程式碼都是以內聯的形式寫入到HTML程式碼中的,程式猿們往往更喜歡通過document.addEventListener或者jQuery中的$('dom').click直接綁定事件。例子如下:
http://127.0.0.1:8000/xss.php?test=alert(1)
document.addEventListener
$('dom').click
1
2
3
4
5
6
7
<script type=“text/javascript”src=“http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js”></script>
<div class=“link-area”></div>
<?php
echo '<script>$(“#image”).click(function(){$(“.link-area”).html(“'.$_GET['test'].'”)});</script>';
?>
所以我們現在需要這樣的介面:能够觸發某個DOM節點的某個事件,包括但不僅限於click事件。PhantomJS和Javascript都可能存在這樣的介面,但是找遍了PhantomJS,甚至是CasperJS的介面,也只是發現了觸發click事件的介面。所以聚焦點重新回到Javascript上來。很快,我們發現了dispatchEvent函數。
dispatchEvent
1
2
3
4
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(click,true,true,null);
document.getElementsByTagName(“div”)[0].dispatchEvent(evt);
成功執行了click事件,但是如何能獲取到所有節點的綁定事件呢?有兩種方法:
- 遍歷所有節點,獲取每個節點綁定的事件
- 在dom節點加載前,重寫addEventListener方法,並將所有的綁定的事件及節點記錄到一個數組中。
addEventListener
方法一在遇到jQuery綁定事件的時候撲街了。方法二明顯比方法一節省資源,並且測試通過。覈心代碼如下:
1
2
3
4
5
6
_addEventListener = Element.prototype.addEventListener
Element.prototype.addEventListener = function(a,b,c){
save_event_dom(this,a);
_addEventListener.apply(this,arguments);
};
這樣,我們的JS程式碼也算告一段落,PhantomJS組件能够執行內聯程式碼及觸發所有的綁定事件。萬事具備,只欠一個調度系統了~
0x05調度系統
XSS掃描是URL細微性掃描,針對網站的每一個連結都要進行測試。XSS檢測系統的輸入值包括:
- URL(如:http://127.0.0.1:8000/xss.php?a=1&b=2)
http://127.0.0.1:8000/xss.php?a=1&b=2
- method
- post_data
- headers
調度系統的功能就是處理這個URL,拼接對應的payload,並調用PhantomJS組件,檢測是否含有XSS漏洞。舉個例子,當payload為<img src=1 onerror=alert(1)>時,需要調用兩次PhantomJS組件,輸入的URL分別為:
<img src=1 onerror=alert(1)>
- http://127.0.0.1:8000/xss.php?a=<img src=1 onerror=alert(1)>b=2
http://127.0.0.1:8000/xss.php?a=<img src=1 onerror=alert(1)>&b=2
- http://127.0.0.1:8000/xss.php?a=1&b=<img src=1 onerror=alert(1)>
http://127.0.0.1:8000/xss.php?a=1&b=<img src=1 onerror=alert(1)>
當然Payload不止一個,會有很多種玩法,簡單提供幾個基礎Payload:
1
2
3
4
5
6
7
8
'“><img src=1 onerror=alert(1)>
'“><script>alert(1)</script>
';alert(1)//
“;alert(1)//
'“onmouseover=alert(1)
javascript:alert(1)
'“></script><img src=1 onerror=alert(1)>
“'></textarea><xsstest>
0x06更多思考
採用了Webkit解析器來檢測XSS漏洞,提高了檢測的覆蓋率,也大幅降低了誤報率。但有些僅在IE下有效的漏洞,就無法覆蓋到了。上述種種,已經基本將動態XSS檢測的思路分析透徹。XSS有很多種玩法,在payload中可以帶進一些有意思的攻擊程式碼,比如釣魚、打Cookie(配合XSS平臺)、甚至探測網絡狀況等等不再贅述。
最後,再次歡迎對XSS利用有各種猥瑣想法的同學來交流,微博@Fr1day