二、不利用数组的删除(1个for循坏)
代码分析
<!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>
// http://127.0.0.1/domfilter/demo6.html#<img src=1 οnerrοr=alert(1)>
const data = decodeURIComponent(location.hash.substr(1)); //截取#后面的值
const root = document.createElement('div'); //创建一个div
root.innerHTML = data; //将data数据用innerHTML赋值给root【innerHTML不执行script】
// 这里模拟了XSS过滤的过程,方法是移除所有属性
for (let el of root.querySelectorAll('*')) //这root同div,而querySelectorAll选取子元
素其中*表示所有的被选中。插入到el中
{
for (let attr of el.attributes) // //获取子元素属性
{
el.removeAttribute(attr.name); }
} //将子元素属性全部删除
document.body.appendChild(root);
</script>
</html>
运行代码
这里发现aaa由于只是一个字符串,而不是一个标签所以不会删除,然后我们试试弹窗在这里能不能运行,根据前面的知识我们知道因为在innerHTML中不执行script,所以我们使用img标签来弹窗。如果全部过滤则不会弹窗,没有过滤成功则会弹窗。
#<img src=1 onerror=alert(1)>
没有进行弹窗操作,明显这个标签的某些属性被删除掉了,那么到底删掉了哪些属性呢,我们来采用断点调试的方法来调试这个程序,将断点设置在for循坏删除标签属性那两句。
代码调试
根据箭头,可以知道date的初值为 "<img src=1 onerror=alert(1)>"
这时候我们可以我们查看到此时el已经获取到了img标签所有属性,当然包括我们输入的src与onerror两个属性,开始第一次的循坏
如图此时它识别到了src,并且有name属性为此removeAttribute将其删除。
后续持续不断下一步发现它并没有对onerror属性进行识别删除,而是直接跳出了循环,这就有意思了,将src这个有用的标签属性删除了。而把危险的标签留了下来。
我们利用python排序的代码再来解释一下这个情况
很神奇的一件事发生了,为啥只要6,5,4 三个数了,1,2,3去哪里了。这里涉及到一个索引的问题
1、首先我们给予的是一个数组,为此在遍历时是一个一个进行。当前判断完就跳下一个,即:例如从1开始,第1个数鉴定完后就到第2个数进行判断
2、明白了判断流程如此就可以说明。当第一次判断从第一位数字1开始,然后删除了max值6,随后第二次判断进入下一位从第二位数字3开始,删除了max值5。再第三次判断从第二位数字4开始删max值4。此时数组剩余1,3,2了,索引的值3,但是现在剩下的索引只能到2于是循坏就结束了。
3、每一次删除数字后的数字都会往前补一位于是就导致有的数是没有被遍历到的
解决问题
1.进循环删除无用数据
就是输入一些没用的数据,让它进行提前进行循坏判断占位,从而让我们真正想执行的语句放到后面,绕过这个循坏判断,实现不会被删除掉的效果
<img a=bbb src=1 c=sdd onerror=alert(1)>
<details open ontoggle=alert(1)> //它是open被过滤了后面的οntοggle=alert(1)替代上了open
2.不进循环
这个方法就是用两次<svg>标签,简单理解就是如果只有一层<svg>标签,那么它在执行的时候会直接return返回,那当是嵌套<svg>的时候,会先执行里面的onload,在执行js,从而阻塞了dmo数的执行。
<svg><svg/onload=alert(1)>
二、利用数组进行删除(两个for循坏)
代码分析
<!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.substr(1));;
const root = document.createElement('div');
root.innerHTML = data;
// 这里模拟了XSS过滤的过程,方法是移除所有属性,sanitizer
//这root同div,而querySelectorAll选取子元素其中*表示所有的被选中。插入到el中
for (let el of root.querySelectorAll('*')) {
// 创建这个数组,用来存放标签的name
let attrs = [];
for (let attr of el.attributes){//获取上面el即所有子元素的属性
attrs.push(attr.name); //把name属性的值放到attrs数组中
}
for (let name of attrs) //获取attrs数组的name值给name用于删除{
}
}
document.body.appendChild(root);
</script>
</html>
运行代码
这里我们同样用img标签进行弹窗操作。同时利用第一种方法试试是否可以进行绕过,发现是失败的,用两个<svg>标签试试,结果是成功的,原理同上述一样。
解决问题
1.进循环删除无用的属性
本题的关键在于把let attr of el.attributes这一步,既然我们要进入循环那么便将被过滤条件判断是否被过滤。而这一步代码的判断决定了那些会被推入到attrs数组中。为此我们要截取取到这一步的数据并修改推入到attrs数组的数据。为此提及这点就要运用dom与window的量子纠缠的知识,并需要一个可迭代对象(组)有以下组合搭配
- form-----button
- form-----input
- form-----objest
- form-----texterea
- form-----img
- form--image
看下面这段代码
<body>
<form id="a" action="">
<img id="a" name="c" />
</form>
<script>
console.log(window.a.c)
</script>
1.上面例如涉及到当id或者name相同时,他规为同一个集合。当归为一个组的时候它们为可迭代对象组。如此利用这个让条件过滤的是from下img的标签,而form标签没有过滤。为此弹窗的语句可以用于form标签中。
2.img如果标签没了就没有图片就无法操作没有触发条件,为此我们可以想到onfocus这个自动点击来触发条件。但是onfocus只在input中使用。所以我们使用form与input满足name相同为同一集合,可是因为input标签的属性全被过滤了,为此触发弹窗语句写在form中。
3.考虑了语句过滤,以及语句存放位置和条件触发onfocus。==这里有个小知识点就是想要触发onfocus属性那么这元素要被允许被聚焦。==因为只有对其聚焦后onfocus的获得焦点时发生的事件才会触发,input是输入框所有自己带了聚焦功能,而要一个元素具有聚焦功能要使用tabindex属性
尝试运行
<form tabindex=1 onfocus="alert(1)" autofocus="true"><input name=attributes><input name=attributes></form>
这种是成功跳出弹窗的,但是因为这个是自动将你的鼠标自动对焦,所以会一直进行弹窗,所以我们可以在它执行成功一次之后将他移除
最终结果
<form tabindex=1 onfocus="alert(1);this.removeAttribute('onfocus');" autofocus="true"><input name=attributes><input name=attributes></form>