XSS的中文名稱叫跨站腳本,是WEB漏洞中比較常見的一種,特點就是可以將惡意HTML/JavaScript代碼註入到受害用戶瀏覽的網頁上,從而達到劫持用戶會話的目的。XSS根據惡意腳本的傳遞方式可以分為3種,分別為反射型、存儲型、DOM型,前面兩種惡意腳本都會經過伺服器端然後返回給客戶端,相對DOM ...
XSS的中文名稱叫跨站腳本,是WEB漏洞中比較常見的一種,特點就是可以將惡意HTML/JavaScript代碼註入到受害用戶瀏覽的網頁上,從而達到劫持用戶會話的目的。XSS根據惡意腳本的傳遞方式可以分為3種,分別為反射型、存儲型、DOM型,前面兩種惡意腳本都會經過伺服器端然後返回給客戶端,相對DOM型來說比較好檢測與防禦,而DOM型不用將惡意腳本傳輸到伺服器在返回客戶端,這就是DOM型和反射、存儲型的區別,所以我這裡就單獨的談一下DOM型XSS。
DOM文檔
為了更好的理解DOM型XSS,先瞭解一下DOM,畢竟DOM型XSS就是基於DOM文檔對象模型的。對於瀏覽器來說,DOM文檔就是一份XML文檔,當有了這個標準的技術之後,通過JavaScript就可以輕鬆的訪問它們了。
下麵舉例一個DOM將HTML代碼轉化成樹狀結構:
<html>
<head>
<meta charset="gbk" />
<title> TEST </title>
</head>
<body>
<p>The is p.<p>
<h1>Product:</h1>
<ul>
<li>Apple</li>
<li>Pear</li>
<li>Corn</li>
</ul>
</body>
</html>
轉化成模型如下圖:
這樣做的好處就是,通過這種簡單的樹狀結構,就能把元素之間的關係簡單明晰的表示出來,方便客戶端的JavaScript腳本通過DOM動態的檢查和修改頁面內容,不依賴服務端的數據。
利用原理
客戶端JavaScript可以訪問瀏覽器的DOM文本對象模型是利用的前提,當確認客戶端代碼中有DOM型XSS漏洞時,並且能誘使(釣魚)一名用戶訪問自己構造的URL,就說明可以在受害者的客戶端註入惡意腳本。利用步驟和反射型很類似,但是唯一的區別就是,構造的URL參數不用發送到伺服器端,可以達到繞過WAF、躲避服務端的檢測效果。
為了更方便大家的理解,下麵我舉幾個場景給大家理解。
場景一:innerHTML
<html>
<head>
<title> DOM-XSS TEST </title>
<style>
#box{width:250px;height:200px;border:1px solid #e5e5e5;background:#f1f1f1;}
</style>
</head>
<body>
<script>
window.onload= function(){
var oBox=document.getElementById("box");
var oSpan=document.getElementById("span1");
var oText=document.getElementById("text1");
var oBtn=document.getElementById("Btn");
oBtn.onclick = function(){
oBox.innerHTML = oBox.innerHTML + oSpan.innerHTML + oText.value + "<br/>";
// oBox.innerHTML += oSpan.innerHTML + oText.value + "<br/>";//這是簡便的寫法,在js中 a=a+b ,那麼也等同於 a+=b
oText.value=""
};
}
</script>
<div id="box"></div>
<span id="span1">小明:</span>
<input type="text" id="text1"/>
<input id="Btn" type="button" value="發送消息" name=""/>
</body>
</html>
第一次是正常訪問:
hellow
第二次是將JavaScript代碼作為參數寫入值中:
<svg/onload=alert(1)>
這裡我只在火狐瀏覽器利用成功,在chrome利用失敗,我猜可能chrome對安全防護做得比較好,這裡不繼續各個瀏覽器版本問題。
使用innerHTML、outerHTML 時要註意,標簽需不進行編碼處理,可能會導致XSS。防護方法就是替換成innerText,它自動將HTML標簽解析為普通文本,所以HTML標簽不會被執行,避免XSS攻擊。
oBox.innerText = oBox.innerHTML + oSpan.innerHTML + oText.value + "<br/>";
場景二:跳轉
<html>
<head>
<title> DOM-XSS TEST </title>
</head>
<body>
<script>
var hash = location.hash;
if(hash){
var url = hash.substring(1);
location.href = url;
}
</script>
</body>
</html>
正常訪問是用#
去實現頁面跳轉,但是因為跳轉部分參數可控,可能導致Dom xss。
通過 location.hash 的方式,將參數寫在 # 號後,既能讓JS讀取到該參數,又不讓該參數傳入到伺服器,從而避免了WAF的檢測。
變數hash作為可控部分,並帶入url中,變數hash控制的是#之後的部分,可以使用偽協議#javascript:alert(1)。常見的幾種偽協議有javascript:
、vbscript:
、data:
等。而現在的移動端(android和ios),都可以自定義這種協議從瀏覽器打開本地app,具體可以看看https://www.cnblogs.com/WuXiaolong/p/8735226.html
#javascript:alert(1)
場景三:eval
#';alert(1);//
直接將用戶輸入數據拼接到代碼里。
eval("var x = '" + location.hash + "'");
場景四:cookie、referrer
從localStorage、SessionStorage、Cookies儲存源中取數據,這些值往往會被開發者忽略,認為這些值都是在瀏覽器獲取的,是安全的,就未進行處理。
var cookies = document.cookie;
document.write(cookies);
場景五:document.write 、document.URL.indexOf("id=")
var ids = document.URL;
document.writeln(ids.substring(ids.indexOf("id=")+3,ids.length));
indexOf獲取url裡面的參數,然後通過writeln( )或者write( )輸出到HTML,造成xss,不過我現在(2020.3)在chrome和firefox瀏覽器測試,write()函數很難利用,除非結合一些特殊場景。
防護策略
還有一些正則匹配缺陷、業務邏輯型缺陷、配合移動端跳轉等、使用第三方前端框架(比如多媒體編輯框)等場景沒有一一進行說明(精力實在有限了...),後期有空可能會繼續補全這些場景。
檢測的流程就是通過查看代碼是否有document.write、eval、window之類能造成危害的地方,然後通過回溯變數和函數的調用過程,查看用戶是否能控制輸入。如果能控制輸入,就看看是否能複習,能複習就說明存在DOM XSS,需要對輸入的數據進行編碼。
代碼審計時審計的特征點(包括但不限於):
var elements = location.hash;
elements.indexOf
var oBtn=document.getElementById("Btn");
oBtn.innerHTML
oBtn.outerHTML
document.createElement
oBtn.setAttribute
oBtn.appendChild
document.write
document.writeln
eval("var x = '" + location.hash + "'");
setTimeout("alert('xss')", 1000)
window.setTimeout
document.setTimeout
window.setInterval
document.execCommand('ForeColor',false,'#BBDDCC');
document.createElement
document.createElementNS
document.createEvent
document.createXxx
js語法很靈活、庫函數也很多,這裡沒法完全列舉全,我覺得需要對js語法體系有一定瞭解,才可能更多的找全這些特征。
當業務需要必須得將用戶輸入的數據放入html,那就要儘量使用安全的方法,比如innerText(),testContent()等。在使用框架時儘量使用框架自帶的安全函數。
參考文檔
《XSS跨站腳本-攻擊剖析與防禦》
《Web漏洞防護》
https://xz.aliyun.com/t/5181
https://domgo.at/cxss/example
https://www.secpulse.com/archives/92286.html