XSS 绕过分析:一次循环与两次循环的区别

目录

代码分析

代码流程:

一次循环的问题

原因分析:删除顺序导致遗漏

两次循环修复方案

两种绕过方式

绕过方法 1:DOM破环

绕过方法 2:SVG XSS(双 SVG 绕过)

1. 为什么 "一个SVG注定失败,两个SVG直接成功"?

2. 为什么属性被删除后SVG仍能触发XSS?

复现对比实验

场景1:单SVG(失败)

场景2:双SVG(成功)

防御建议:


代码分析




    
    
    
    Document


    

代码流程:

  1. 通过 decodeURIComponent(location.hash.substr(1)) 获取 URL # 号后面的内容。
  2. 创建一个 div,并将获取的内容赋值给 div.innerHTML
  3. 遍历 div 内部的所有子元素,并移除它们的所有属性。
  4. div 添加到 document.body

一次循环的问题

测试 Payload:


意外结果: src=1 被删除,但 onerror=alert(1) 仍然存在。

原因分析:删除顺序导致遗漏

  1. for (let attr of el.attributes) 遍历元素的属性。
  2. src 作为第一个属性被删除后,属性列表的索引发生变化。
  3. onerror 变成第一个属性,而 for 循环仍在进行,导致跳过了 onerror

绕过方法:


通过增加 test=aaatitle=bbb 等属性,可以让 onerror 处于不会被跳过的位置,从而绕过一次循环的属性删除。


两次循环修复方案

改进代码:


两次循环的改进点:

  • 第一次循环 先收集所有属性的名称。
  • 第二次循环 再删除这些属性,避免索引错位问题。

两种绕过方式

绕过方法 1:DOM破环

原理:onfocus 事件先触发,移除 onfocus 后,仍然可以执行 alert(1)


  
  

过程:

  1. 被解析时,tabindex=1 使其获得焦点。
  2. autofocus="true" 使 立即聚焦。
  3. onfocus 触发 alert(1),并删除自身的 onfocus
  4. 代码执行时,属性删除已经无法影响 XSS 触发。

绕过方法 2:SVG XSS(双 SVG 绕过)


1. 为什么 "一个SVG注定失败,两个SVG直接成功"?

关键原因:浏览器解析的时序差异

  • 单SVG场景

    1. 当插入DOM时,浏览器会立即解析SVG并开始加载href资源。

    2. 资源加载是异步的onload事件会在资源加载完成后触发。

    3. 此时代码中的属性删除循环(el.removeAttribute)可能已经执行完毕,移除了onload属性。

    4. 事件未被绑定:因为属性在事件触发前已被删除,XSS无法触发。

  • 双SVG场景

     
     
    1. 浏览器解析第一个SVG时,会同步处理其子元素。

    2. 当解析第二个SVG时,浏览器可能已经进入异步任务队列

    3. 事件触发时机提前:第二个SVG的onload事件可能在属性删除循环完成前触发。

    4. XSS成功:事件回调在属性被删除前已绑定到DOM元素。

本质

  • 多个SVG可能改变浏览器的事件循环时序,导致属性删除和事件触发的竞争条件(Race Condition)。


2. 为什么属性被删除后SVG仍能触发XSS?

关键原因:事件触发与属性删除的异步性

  • 示例代码

    1. 插入DOM阶段:浏览器解析SVG并立即触发onload事件。

    2. 事件触发是异步的:即使后续通过removeAttribute删除了onload属性,但事件回调已经在事件队列中等待执行。

    3. 执行顺序

      root.innerHTML = data;       // 插入SVG,触发onload(事件进入队列)
      deleteAllAttributes();       // 删除onload属性
      // 事件队列中的回调仍会执行!

技术细节

  • 事件绑定与属性无关onload="..."内联事件处理函数,浏览器在解析HTML时会直接将其转换为事件监听器,而不是依赖属性存在与否。

  • 属性删除仅移除了HTML特性,但无法撤销已经注册的事件回调。


复现对比实验

场景1:单SVG(失败)
// 插入内容:
root.innerHTML = data;
// 浏览器解析SVG,触发onload(事件进入队列)
deleteAllAttributes(); 
// 事件回调尝试执行时,发现image元素已无onload属性,可能被浏览器忽略
场景2:双SVG(成功)
// 插入内容:
root.innerHTML = data;
// 第一个SVG解析完成(同步)
// 解析第二个SVG时,可能已进入异步队列
deleteAllAttributes(); 
// onload事件在删除前已绑定,回调执行成功

防御建议:

  1. 彻底禁用 innerHTML
    document.body.textContent = data;
    
  2. 使用 DOMPurify 进行过滤
    import DOMPurify from 'dompurify';
    root.innerHTML = DOMPurify.sanitize(data, { FORBID_ATTR: ['onload', 'onfocus'] });
    
  3. 隔离 SVG 解析
    const svgContainer = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svgContainer.innerHTML = data;
    

你可能感兴趣的:(渗透攻击,xss,前端)