什么是 DOM?
文档对象模型 (DOM) 是 Web 文档的编程接口。DOM 将文档表示为节点和对象; 这样,编程语言就可以与页面交互。作为网页的面向对象表示形式,可以使用脚本语言(如 JavaScript)对其进行修改。
原理
<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);
</script>
执行后会发现除了document情况下不能通过id取到标签外,其余方式都可以通过id和name取到标签。
因为文档对象模型(DOM)允许JavaScript对其进行操作。我们可以对HTML插入特定id或name的标签,来达到执行命令时使命令错误的指向我们自定义标签的目的。
例如document.cookie 是用来创建 、读取、及删除cookie,如果我们创建一个<img name="cookie">
标签,那么执行document.cookie时就会取到这个标签。
执行下面这段代码,本意是向body里插入一个div标签
<body>
<form name="body">
<img id="appendChild">
</form>
</body>
<script>
var div = document.createElement('div');
document.body.appendChild(div)
</script>
执行后报错Uncaught TypeError: document.body.appendChild is not a function
,可以看到我们多层覆盖掉了document.body.appendChild ⽅法。
如何利用Dom Clobbering?
以上举的例子中我们创建或者覆盖都是标签,类型为HTMLElment对象,我们需要将其转换为⼀个可控的字符串类型,以便我们进⾏操作。
因此我们需要在创建标签时调用toString方法将其转换成字符串类型的标签。但并非所有标签的toString方法都对我们有用
Object.prototype.toString.call(document.body.appendChild)
'[object Function]'
显然上面的字符串就没用。
我们需要在window全局变量下所有元素中寻找我们需要的特定标签,需求为自身重写toString方法的标签。
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属性来进行字符串转换。
<a>
的toString方法会调用自身的href属性。
针对dom破坏有一道xss的题目:https://xss.pwnfunction.com/warmups/ok-boomer/
<!-- Challenge -->
<h2 id="boomer">Ok, Boomer.</h2>
<script>
boomer.innerHTML = DOMPurify.sanitize(new URL(location).searchParams.get('boomer') || "Ok, Boomer")
setTimeout(ok, 2000)
</script>
我们可以这样利用
https://xss.pwnfunction.com/warmups/ok-boomer/boomer=<a id="ok" href="tel:alret(1)">
需要注意的是:dom破坏适用于覆盖的标签本身没有的情况,如果上面的ok
在HTML中本来存在那么就不会成功,当然我们也可以想办法让本身存在的代码失效,再进行dom破坏。
两层结构调用
构建集合关系
<div id=x>
<a id=y href='111'></a>
</div>
<script>
alert(x.y);
</script>
这里无论第⼀个标签怎么组合,得到的结果都只是undefined。但是我们可以通过另⼀种⽅法加⼊引入name属性就会有其他的效果。
我们可以通 过先构建⼀个HTMLCollection
,再通过collection[name]
的形式来调⽤。
<div id="x">
<a id="x" name=y href="111"></a>
</div>
<script>
alert(x.y);
</script>
利用原本存在的层级关系
利用HTML标签之间存在的关系来构建层级关系
以下关系可以用来构建层级关系: form->button
、form->fieldset
、form->image
、form->img
、form->input
、form->object
、form->output
三层结构调用
结合上面的两种方法可以构建出三层调用,先用⼀个HTMLCollection 获取第⼆级,再在第⼀个表单中用output 标签即可。
<form id="x" name="y"><output id=z>I've been clobbered</output></form>
<form id="x"></form>
<script>
alert(x.y.z.value);
</script>