当前位置:网站首页>DOM破坏及复现实验
DOM破坏及复现实验
2022-08-02 03:06:00 【橙栎】
DOM与window的量子纠缠
举一个例子,给button的标签id设为btn,然后在script标签中使用window.btn来抓取id=btn的值。通过结果可以看到,window.btn是将整个标签全部都抓取了出来。
这个也就是DOM和window的量子纠缠,也就是说:在HTML中设定了一个有id的元素在之后,可以在js中就直接操作,并且由于js的作用域的规则,这也可以直接使用btn,因为如果在当前的作用域找不到的情况下,他会一直往上去寻转,直到找到为止。
并且在HTML的说明文档中有明确说明中有提到节选的重点:
选中的位置就是所提及的重点;总结来说就是**除了id可以直接使用window存取到以外;embed、form、img和object这四个标签也可以使用name进行操作**也就是说可以使用HTML的元素来影响js。
DOM Clobbering(DOM破坏)
刚刚所说的利用HTML元素来影响js这个就是可以被理解为DOM破坏。也就是通过DOM来把一些东西给覆盖掉的方法。
下面这个代码就是:如果你存在test1和test2这两个元素,就执行这俩个元素。但是这次是两个元素,不是一个了。
if (window.test1.test2){
eval(''+window.test1.test2)
在这之前存在两个问题:
1.利用html标签的属性id,很容易在window对象上面创建任意的属性,但是我们能在新对象上创建新属性么?也就是说不在window下创建,而是在form标签,p标签等去创建。
2.怎样控制DOM elements被强制转为string之后的值,大多数的dom节点被转为string后是[object HTMLInputElement]。
首先,在js中,每一个函数几乎有两个方法,分别时valueof方法和tostring方法
valueof方法是将返回报装对象实例对应的原始类型的值
tostring方法返回的是对应的字符串的形式
对象的valueof方法总是返回对象自身,这种时候在调用对象的tostring方法,将它转换为字符串,比如说:
对象的tostring默认返回的方式是[object,Object].
但是我们在测试的时候可以看到:
我们在form表单中创建id的属性值是test1,然后在input标签中创建name属性值是test2.使用alert进行
这样的话它的运行结果是会弹窗的,但是返回的并不是两个object,而是这样的,像一个字符串,但是我们是需要使用eval执行的,执行这样的数组就是没有意义的。而需要执行的是一个函数。
针对于上面提到过的第二个问题,有下面这样的代码,它可以遍历html中所有可能的元素并且检查它们的tostring方法是否继承自object.prototype或是以另一种方式定义。
getOwnerPropertyNames:遍历object在window下的所有属性。
可以看到它第一个返回的是area,另一个是a标签。在a标签中tostring方法只返回一个href属性值。所以刚刚前面举的例子的解决方案就是,设我们有两个相同的id的值。分别是:
<a id =test1>click!</a>
<a id =test1>click2</a>
因为给的id的值都是相同的,所以在给出的结果的值是HTMLCollection,并且组成了一个类似于数组的样子。
也就是说。我们可以通过index以及访问其中特定元素的id,也就是说window.test1.test1实际上是指第一个元素。同时,设置name属性也可以在htmlCollection中。
也就是说在刚刚的标签中在加一个name的属性。
<a id =test1>click!</a>
<a id =test1 name=test2>click2</a>
它的结果就会把元素所以在的标签给取出来。
基于以上复现实验
第一个:单个循环,移除标签的属性。
下面代码是:首先截取#号后面的值,然后创建一个div,然后将#号后面的值都赋值给div,然后使用querySelectorAll选取div下所有的子元素;然后获取子元素的属性,并将属性全部删除,但是我们在运行的时候会发现一个问题。
<script>
const data = decodeURIComponent(location.hash.substr(1));
const root = document.createElement('div');
root.innerHTML = data;
for (let el of root.querySelectorAll('*')) {
for (let attr of el.attributes) {
el.removeAttribute(attr.name);
}
}
document.body.appendChild(root);
</script>
我们明明输入的是:<img src=1 οnerrοr=alert(1)>,但是运行后得出的结果是它只删除了一个src,把onerror留了下来。
这里使用调试方法来查看它每一步运行的值。
我们现将断点放在 for (let el of root.querySelectorAll('*')) {
和for (let attr of el.attributes) {
这两行上,然后逐步往下走,可以在右边的作用域中看到它的data是:<img src=1 onerroe=alert(1)>
获取到了这个,
然后我们就接着往下走。然后可以看到el获取的值是img,也就说说它已经成功获得到了这个标签。
然后接着继续走;然后attr获取到了第一个元素:src,之后,它执行了下面的移除操作。
接下来按照预期应该是回到标签内继续匹配元素onerror然后进行删除,但是当继续下一步的时候,attr取到的值是空的,就直接跳出循环,直接结束。
按照我们的思维这中方式应该是正确的,但是,在进行循环的过程中,attr首先匹配到的是src元素,然后在循环过后直接删除,删除了之后,剩余的哪个onerror自动往前移动,就好像做核酸一样,拍你前面的那个人因为没有带口罩被撵走了,那你自然是要往前走一位来替代它的位置。这个也是,onerror替代了src排的第一个的位置,它就变成了第一个,但是我在刚刚循环的时候,已经把第一个给循环了,我要去循环下一个的时候它没有了,所以我就结束循环了。
所以要破解这关的话就是将元素的顺序打乱,根据他的漏洞来让他删除后所剩下的是我们所需要的就可以了。
就是将src元素写道第2为,在删除第一位的时候它变成第一位就会保留了,然后将onerror保存到第四位,那样在删除第一位后,原来的第三位变成了第二位,第四位就变成了第三位,,第二位运行完后被删除,最开使的第四位就变成了第2位,但是我们已经执行完第二位了,所以后面就没有了,他就跳出了循环。
总结来说:
第二个:两个循环,移除标签的属性
由于刚刚那个代码是存在漏洞的,所以进行了修改,分成了两个步骤,第一个循环进行存放,第二个循环进行删除,解决了刚刚的问题。
const data = decodeURIComponent(location.hash.substr(1));;
const root = document.createElement('div');
root.innerHTML = data;
// 这里模拟了XSS过滤的过程,方法是移除所有属性,sanitizer
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);
这个循环也确实会将所有的元素给过滤掉。
可以看到输入的<img src=1 οnerrοr=alert(1)>里面的元素都没了,只剩了一个img标签。
解决这个问题,我们可以从两个方向来进行攻略
1.进入循环但是让他删除的是没有用的数据
这种方式就可以使用到刚开始讲的那个DOM破坏的方式来进行。
el执行的是attr,如果有一个元素可以劫持这个,那么删除的就不是atr而是里面的一个子元素。
现测试一个例子:
<body>
<form id="x"action="">
<img name="attributes">
</form>
</body>
<script>
console.log(window.x.attributes)
</script>
执行上面代码的时候,的出来的结果是可以打印出来整个标签的。
也就是说,这里的x是上面的el;插入一个form之后,这个el就相当于是等于这个form的,而那个el.attributes相当于是哪个img。也就是让img进入循环,而在form中进行触发,这样就实现了进入循环,但是删除的是无用的标签。
所以可以使用刚才的方法测试:
http://localhost//demo4/demo4.html#%3Cform%20action=%22%22%3E%3Cimg%20id=attributes%3E%3C/form%3E
但是它的结果显示是:el.attributes不是一个可迭代器,
可迭代对象有一个特征就是for循环,现在进入的只有一个元素,他是循环不了的,所以我们需要将他组成数组或者集合,
而刚刚我们刚刚正好说了,如过id的值是相同的话会组成一个集合,而这个就刚好满足了刚刚咱们所需要的。所以它就可以写成下面的形式:
<form id="x"action="">
<img name="attributes">
<img name="attributes">
</form>
这样的话,img标签就进入了循环删除,这样的话我们form里面还缺少一个触发的属性,而onfocus属性正好可以自动触发,但是它不是form属性,而是input下面。我们也可以将img换成input,这样也可以满足name相同的时候会变成一个集合。
这个解决之后还需要自动聚焦,这个时候就需要一个自动聚焦的属性。
tabindex:全局属性,以及它是否(在何处)参与顺序键盘导航。
加上tabindex属性的话就可以把焦点聚集在input上,否则onfoucus是没有办法实现的。
所以这样的话:我们就可以进行尝试:
<form tabindex=1 onfoucus=“alert(1)” autofocus><input name=attributes><input name=attributes>
这种是成功跳出弹窗的,但是因为这个是自动将你的鼠标自动对焦,所以会一直进行弹窗,所以我们可以在它执行成功一次之后将他移除。
<form tabindex=1 onfoucus=“alert(1);this.remove.Attribute(‘onfocus’)” autofocus><input name=attributes><input name=attributes>
2.不进入循环
这里使用的是两个svg标签,也就是使用\<svg>\<svg onload=alert(1)>
来尽行绕过,它可以在过滤代码之前进行绕过,也就是说,它在代码的root.innerHTML = data;
就已经执行了。
要解释这个的话首先要了解以下浏览器的渲染过程。
也就是在DOM树构建完成之后,会触发DOMContentLoaded事件,接着就会加载脚本或者图片,然后执行全部加载完成后会触发load事件。
使用img标签失败的原因是:当我们使用断点测试的时候,首先先将过滤给注释掉,然后我们将断点放在root.innerHTML = data;上,然后进行一步一步的测试:
第一步:往下走的第一步的时候它会直接走到最后,但是img标签并没有执行。
在往下走的时候才会执行img并且可以弹窗
也就是说,如果加上过滤的话,它是先循环过滤了才可以进行弹窗,但是经过过滤后子元素就被过滤掉了。也就是说js阻塞了DOM树的构建;也可以说在script标签内的JS执行完毕以后,DOM树才会构建完成,
而svg执行的时候就是会先执行;和img测试的方式相同,在同样的位置进行断点测试。
在 root.innerHTML = data;位置的时候,往下走可以看见它会直接进入到alert()然后弹窗。
点击下一步的时候,它会直接进入到alert并且进行弹窗。
这样的话,它没有进入到循环删除就已经可以进行弹窗。接着往下走的话的话即便被删它也已经执行过了,所以也没有必要了。
也就是说,这种嵌套的svg成功的原因是因为当页面为root.innerHtml赋值的时候浏览器进入DOM树构建过程;在这个过程中会触发非最外层svg标签的load
事件,最终成功执行代码。
边栏推荐
猜你喜欢
MySQL8 -- use msi (graphical user interface) under Windows installation method
第一章——线性表(顺序表和链表)
How ReentrantLock works
MySQL8--Windows下使用msi(图形界面)安装的方法
iVX低代码平台系列详解 -- 概述篇(二)
STM32——LCD—TFTLCD原理与配置介绍
22-08-01 西安 尚医通(01)跨域配置、Swagger2、R类、统一异常处理和自定义异常、Logback日志
第二章——堆栈、队列
MySQL8.0.28 installation tutorial
aws s3 upload file
随机推荐
什么是轮式里程计
MySQL8--Windows下使用msi(图形界面)安装的方法
【LeetCode】144.二叉树的前序遍历
7-42 整型关键字的散列映射 (25 分)
* Compare version numbers
【LeetCode】83.删除排序链表中的重复元素
[LeetCode] 94. Inorder traversal of binary tree
7、MySQL Workbench 导出导入数据库
Week 304 Dunk
2022年最新一篇文章教你青龙面板拉库,拉取单文件,安装依赖,设置环境变量,解决没有或丢失依赖can‘t find module之保姆教程(附带几十个青龙面板脚本仓库)
Chapter 11_Database Design Specifications
py0_二十一天计划书
#{}和${}的区别
启发式合并、DSU on Tree
iVX低代码平台系列详解 -- 概述篇(二)
WebShell特征值汇总与检测工具
剑指 Offer 14- I. 剪绳子
IPIDEA的使用方式
Kubernetes 基本概念
总体写作原则