一、前言
繼XSS基礎篇后,我們知道了三種類型的XSS,這篇文章主要針對DOM型XSS的原理進行深入解析。
二、DOM型XSS原理
2.1、什么是DOM
以一個形象的比喻:
網頁就像是一座房子,而 **DOM** 就是這座房子的“藍圖”或者“結構圖”。房子的每個部分(如門、窗、墻壁)都代表網頁上的某個元素,而 **DOM** 就是用來描述這些元素的工具,使得你可以清楚地知道哪里是門、哪里是窗,甚至改變它們的位置或大小。
所以通過DOM可以得到或者修改網頁標簽的屬性。
舉例:
<h1 id="header">Hello World</h1>
<button id="changeHeader">Change Header</button>
<script>// 獲取 h1 標簽和按鈕let header = document.getElementById('header');let changeHeaderButton = document.getElementById('changeHeader');// 給按鈕添加點擊事件changeHeaderButton.addEventListener('click', function() {// 修改 h1 標簽的文本內容header.innerText = 'New Header Text';});
</script>//點擊按鈕時,<h1> 標簽的文本內容會從 "Hello World" 改為 "New Header Text"。
2.2、DOM破壞
DOM 破壞(DOM Clobbering)是指在 HTML 中,某些屬性、元素的 ID 或名稱等與瀏覽器自帶的 DOM 對象(如 JavaScript 中的全局變量)發生沖突,導致原本應該是 DOM 元素的引用被覆蓋或者破壞的現象。
也就是說,我們插入到瀏覽器的數據被接受并覆蓋網頁標簽中本來就有的信息,那這就會造成DOM破壞。
舉例:
這個例子可以看出原本是沒有cookie這個屬性的,然后我創建了一個div,再創建一個img,里面包含一個name屬性,值為cookie。
接著把img放入div,把div放入document.body下,再調用document.cookie發現獲取了這個img標簽,這就說明document.cookie已經被我們用img標簽給覆蓋了。
這個例子可以看出原本是沒有cookie這個屬性的,然后我創建了一個div,再創建一個img,里面包含一個name屬性,值為cookie。
接著把img放入div,把div放入document.body下,再調用document.cookie發現獲取了這個img標簽,這就說明document.cookie已經被我們用img標簽給覆蓋了。
實例(xss game第八關):
<h2 id="boomer">Ok, Boomer.</h2>
<script>boomer.innerHTML = DOMPurify.sanitize(new URL(location).
searchParams.get('boomer') || "Ok, Boomer")
//這里直接用DOMPurify框架過濾,那想直接繞過這個過濾顯然不太可能setTimeout(ok, 2000)//此時我們試著從這個位置入手
</script>
setTimeout可以接受函數或字符串作為參數并執行它。
在這里,ok變量被執行,但它不存在;這里的JS代碼是沒有任何關于ok參數的定義的,所以我們可以使用DOM破壞,通過DOM破壞,將HTML元素注入到DOM中。
構造ok參數,因為setTimeout函數執行字符串,所以需要用到<a>或者<textarea>標簽,因為這兩個標簽自帶ToString方法。
payload:
?? ?<a id=ok href="tel:alert(1)">a
? ? ? ??
當我創建這個<a>標簽時,瀏覽器會自動在javascript中創建一個ok變量,當setTimeout(ok, 2000)運行時通過id=ok找到這個標簽時<a>標簽會調用ToString()方法,返回href中的值“tel:alert(1)”到setTimeout()中,最后setTimeout()將執行其中的語句。
這里需要注意的是我們要用DOMPurify框架白名單中的tel來替換javascript。
2.3、多層DOM破壞:
繼XSS-GAME的DOM破壞之后,思考如果需要覆蓋的數據是雙層結構(x.y)該怎么辦呢?
方法一、通過自定義方法寫入雙層字符 HTMLCOLLECTION:
<div id="x"><a id="x" name="y" href="aaaa:1111"></a></div>
</body>
<script>alert(x.y);
</script>#這里實際上是創建了一個htmlcollection集合,然后通過collection[name]的方式來調用。
方法二、使用篩選出支持雙選的標簽:
form-button
form-fieldset
form-image
form-img
form-input
form-object
form-output
form-select
form-textarea
構建雙層:
<from id="x"><output id="y">雙層級</output><script>alert(x.y.value);
</script>
構建三層:
<form id="x" name="y"><output id="z">三層級</output></form>
<form id="x"></form><script>alert(x.y.z.value);
</script>