目錄
1 DOMPurify
1.1 漏洞源碼
1.2 加載框架
?編輯
setTimeout
1.3 ok?
1.4 window和document
1.5 Overwrite(document.x)
1.6?Overwrite2(document.x.y)
1.6.1 form表單
1.7 toString
2. 設計目的:“重寫類型轉換邏輯”
3. 延伸:“類型轉換” 的場景
1.8 可控字符串a標簽的屬性等
1.8.1 a href
逐步驟分析結果由來:
最終結果的本質:
?打印代碼結果:
1.9 alert執行script
1.9.*? 和alert的區別
1.10 利用DOM破壞
1.10.1 ok
1.10.2 payload:
1.10.3 setTimeout
?1.11 DOM框架過濾了JavaScript惡意代碼
1 DOMPurify
1.1 漏洞源碼
<h2 id="boomer">Ok, Boomer.</h2>
<script>boomer.innerHTML = DOMPurify.sanitize(new URL(location).searchParams.get('boomer') || "Ok, Boomer")setTimeout(ok, 2000)
</script>
1.2 加載框架
Release DOMPurify 3.2.6 · cure53/DOMPurify · GitHub
可以去GitHub上面將此框架下載下來,加載到這個代碼中,并且加載路徑寫到js之前執行
setTimeout
//這是一個定時器,js定時器有兩個,一個是setTimeout 在指定延遲的時間內執行一次,2秒后執行ok
// setInterval 循環執行
1.3 ok?
1 ok從哪里來的,沒有進行定義?
2 所以ok大概率是我們的payload惡意代碼
3 框架如何過濾呢?(這個框架非常的安全基本上沒有繞過的可能性)
4 ok引入
所以思考:我們該如何創建一個OK。讓setTimeout執行呢?
1.4 window和document
<body><img id="x"><img name="y">
</body>
<script>console.log(x);console.log(y);console.log(document.x);console.log(document.y);console.log(window.x);console.log(window.y);
window是js所有元素的祖先
document是整個頁面
訪問看一下打印運行的結果
1.5 Overwrite(document.x)
1.5.1 打印cookie
自帶的cookie值,網站:
最開始是沒有cookie的
1.5.2 給他重新定義,寫一個cookie
<body></body>
<script>let div = document.createElement('div')div.innerHTML = '<img name=cookie>'document.body.appendChild(div)console.log(document.cookie)
</script>
</html>
再次訪問頁面的cookie
改變了系統自帶的cookie值
所以直接覆蓋掉了系統函數的document.cookie
1.6?Overwrite2(document.x.y)
1.6.1 form表單
<body><form name="body"><img id="appendChild" src=1 onerror="alert(1)"></form>
</body>
<script>console.log(document.body.appendChild)
</script>
?
訪問彈一,并且取代了系統函數打印出來是這個
1.7 toString
按照以上兩種我們獲取的都是html的標簽elment元素的值,但是其實對于大多數的情況我們需要的應該是一個可控的字符串的類型
-
toString()
?是什么?
是 JavaScript 等語言中?對象的內置方法(繼承自?Object.prototype
),默認行為是返回類似?[object 構造函數名稱]
?的字符串(比如?{}.toString()
?返回?[object Object]
,[].toString()
?返回?[object Array]
)。 -
“派生類” 指什么?
指?繼承自父類的子類(比如自定義類?class Dog extends Animal
?中,Dog
?就是?Animal
?的派生類)。
2. 設計目的:“重寫類型轉換邏輯”
-
默認邏輯的不足:
原生?Object.prototype.toString()
?只反映對象的 “類型標簽”(如?Object
/Array
),但實際開發中,我們希望對象轉成字符串時?攜帶更具體的信息(比如?person.toString()
?返回?"{name: 'Alice', age: 20}"
)。 -
重寫的作用:
通過在?派生類中重寫?toString()
,可以自定義?“對象 → 字符串” 的類型轉換規則。例如:javascript
class Person {constructor(name) { this.name = name; }// 重寫 toString,自定義字符串轉換邏輯toString() { return `Person: ${this.name}`; } } const alice = new Person('Alice'); console.log(alice.toString()); // 輸出 "Person: Alice"(而非默認的 "[object Object]")
3. 延伸:“類型轉換” 的場景
toString()
?會在以下?隱式類型轉換場景?中被自動調用:
- 拼接字符串時:
console.log('Hello ' + alice)
(等價于?'Hello ' + alice.toString()
)。 - 使用?
String()
?函數轉換對象:String(alice)
(內部調用?alice.toString()
)。
因此,重寫?toString()
?本質是?定制對象在這些場景下的表現,屬于 “類型轉換邏輯” 的一部分。
簡單總結:toString()
?讓對象能自己決定 “怎么變成字符串”,派生類重寫它,就是為了讓轉換結果更符合業務需求
要將基本的 Object.prototype.toString() 用于重寫的對象(或者在 null 或 undefined 上調用它),你需要在它上面調用 Function.prototype.call() 或者 Function.prototype.apply(),將要檢查的對象作為第一個參數傳遞(稱為 thisArg)。
1.8 可控字符串a標簽的屬性等
1.8.1 a href
a標簽的href屬性里面可以輸入可控的字符串,所以我們可以利用這個來進行,來獲取heref的內容
所以我們可以通過以下代碼來進行 fuzz 得到可以通過 toString 方法將其轉換成字符串類型的標簽
Object.getOwnPropertyNames(window).filter(p => p.match(/Element$/)).map(p => window[p]).filter(p => p && p.prototype && p.prototype.toString !== Object.prototype.toString)
逐步驟分析結果由來:
-
Object.getOwnPropertyNames(window)
先獲取?window
?對象的所有 “自有屬性名”(字符串形式),包括可枚舉和不可枚舉屬性(如各種 DOM 構造函數、全局方法等)。 -
.filter(p => p.match(/Element$/))
篩選出屬性名?以 “Element” 結尾?的字符串。
原因:DOM 元素的構造函數通常以此結尾(如?HTMLElement
、HTMLDivElement
、HTMLImageElement
、SVGElement
?等)。 -
.map(p => window[p])
將上一步篩選出的屬性名,轉換為對應的?window
?對象屬性值 —— 即?DOM 元素構造函數本身(如?window.HTMLDivElement
?對應?HTMLDivElement
?構造函數)。 -
.filter(p => p && p.prototype && p.prototype.toString !== Object.prototype.toString)
最終篩選出滿足以下條件的構造函數:p
?存在(排除 null/undefined);p.prototype
?存在(構造函數有原型對象);- 原型上的?
toString
?方法?不是?Object
?原生的?toString
(即該方法被重寫過)。
最終結果的本質:
返回所有?“名稱以 Element 結尾” 且 “原型重寫了 toString 方法” 的 DOM 元素構造函數。
這些構造函數對應的 DOM 元素(如?<div>
、<img>
?等),其?toString
?行為已被定制(而非使用?Object
?的默認實現),例如?HTMLDivElement
?實例的?toString()
?可能返回更具體的字符串(而非?[object Object]
)。
?打印代碼結果:
分析知道有以下兩個:
1.9 alert執行script
<a id="a" href="aaaa"></a>
console.log(a)
結果:
1.9.*? 和alert的區別
直接alert(a),他將我們的href屬性以string的形式給打印了出來
所以他用alert直接調用了a里面的tostring方法,將href里面的值打印了出來。
你將a當作函數的參數的時候,會自動調用a的tostring方法,而a的tostring是自身方法,不是繼承的,他可以打印herf中的內容
1.10 利用DOM破壞
1.10.1 ok
通過以上的分析和拓展我們應該有了一個大概的思路,首先回到1.3:
1 ok可以從a標簽的id來,因為通過打印a標簽的id可以直接將a標簽打印出來
1.10.2 payload:
2 惡意代碼payload:<a id=ok href=javascript:alert(1)>
1.10.3 setTimeout
3 這是個函數,查看官方文檔解釋:他可以執行字符串,就是說我的ok當傳入函數的參數,利用a標簽自帶的tostring方法將href里面的值當字符串傳遞給setTimeout進行執行
當輸入這個惡意代碼后,調試可以知道,它將JavaScript偽協議給過濾了:
?1.11 DOM框架過濾了JavaScript惡意代碼
框架白名單:
?那我們思考是否可以利用白名單進行繞過呢,那白名單是否可以彈窗呢?試一下
ok,成功繞過?