HTTP请求走私

简介

HTTP请求走私是一种干扰网站处理从一个或多个用户接收的HTTP请求序列的方式,用以绕过安全控制并获得未经授权的访问,执行恶意活动,最早在 2005 年被提出

成因

在正常情况下用户发出的 HTTP 请求的流动如下图:

可以看到在长度标记正常的情况下,蓝色和绿色的数据包被正确的区分,不同用户的数据在后端被正确还原

在整个过程中,最关键的是前置服务器和后端服务器应当在 HTTP 请求的边界划分上达成一致,否则就会导致下图所示的异常:

(图中橙色的部分为走私的内容)

当我们向代理服务器发送一个比较模糊(接收到的实际长度和报文中标的长度不同)的 HTTP 请求时,由于两者服务器的实现方式不同,可能代理服务器认为这是一个 HTTP 请求

即前端服务器和后端服务器对客户端传入的数据理解不一致的情况,即 Content-LengthTransfer-Encoding 标头。然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,当该部分对正常用户的请求造成了影响之后,就实现了 HTTP 走私攻击

Content-Length和Transfer-Encoding

Content-Length

HTTP状态码,代表 HTTP消息长度, 用十进制数字表示. 若Content-Length比实际消息长度短, 请求被截断, 而且下一个请求解析出现错乱.。Content-Length比实际消息长度长,请求将无响应直到超时

Transfer-Encoding: chunked

代表数据以一系列分块的形式进行发送.。Content-Length 首部在这种情况下应该不被发送。 在每一个分块的开头需要添加当前分块的长度, 以十六进制的形式表示,后面紧跟着 \r\n(回车换行) , 之后是分块本身, 后面也是\r\n。 终止块是一个常规的分块, 不同之处在于其长度为0。后面跟两个\r\n

分类

  • CL-TE:前端服务器使用 Content-Length 头,后端服务器使用 Transfer-Encoding

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

所谓CL-TE,顾名思义就是收到包含Content-Length和Transfer-Encoding这两个请求头的请求时,前端代理服务器按照Content-Length这一请求头定界,而后端服务器则以Transfer-Encoding请求头为标准

1
2
3
4
5
6
7
POST / HTTP/1.1
Host: example.com
Content-Length: 16
Transfer-Encoding: chunked
0

chunkedcode

前端服务器处理Content-Length头并确定请求主体长度为16个字节,直到chunkedcode结束。此请求将转发到后端服务器

后端服务器处理Transfer-Encoding标头,因此将消息体视为使用分块编码。它处理第一个块,它被称为零长度,因此被视为终止请求。缓冲区内还剩下chunkedcode,由于存在pipeline技术,后端服务器将这些字节视为队列中下一个请求的开始

如果此时其他用户此时发送了一个 GET 请求,就会与 chunkedcode 拼接成一个畸形的 chunkedcodeGET,造成服务器解析异常

  • CL-CL:前端和后端服务器都支持 Content-Length 标头

攻击者可以恶意构造一个特殊的请求

1
2
3
4
5
POST / HTTP/1.1
Host: example.com
Content-Length: 5
123
A

后端服务器获取到的数据包长度为5。当读取完前5个字符后,后端服务器认为该请求已经读取完毕,然后发送出去。而此时的缓冲区去还剩余一个字母 A,对于后端服务器来说,这个 A是下一个请求的一部分,但是还没有传输完毕

此时恰巧有一个其他的正常用户对服务器进行了请求,则该A字母会拼凑到下一个正常用户请求的前面,攻击在此展开

  • TE-CL:前端服务器使用 Transfer-Encoding 标头,后端服务器使用 Content-Length 标头。

这种情况则属于前端服务器处理Transfer-Encoding请求头,而后端服务器处理Content-Length请求头

构造数据包

1
2
3
4
5
6
POST / HTTP/1.1
Host:example.com
Content-Length: 3
Transfer-Encoding: chunked
chunkedcode
0

前端服务器处理Transfer-Encoding请求头,因此将消息体视为使用分块编码,处理第一块时,有11个字节,直到chunkedcodede的最后一个字节。开始处理第二个块,第二块是0个字节,视为终止请求。此时把请求转发到后端。而后端则在11处完成了对第一个数据包的读取,chunkedcodern0为下一个数据包的开始部分

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

前端服务器处理第一个Transfer-Encoding请求头,后端服务器处理第二个Transfer-Encoding请求头。

构造数据包

1
2
3
4
5
6
Host:example.com
Content-length: 3
Transfer-Encoding: chunked
Transfer-encoding: error
chunkedcode
0

这里是用了两个Transfer-Encoding 字段,并且第二个 TE 字段值为错误值,这里 前端服务器选择对第一个 Transfer-Encoding进行处理,整个请求正常,原封不动转发给后端服务器,而后端服务器则以第二个Transfer-Encoding 字段进行优先处理,而第二个Transfer-Encoding 字段非标准值,根据RPC规范,则会取Content-Length字段进行处理,这样这个请求就会被拆分为两个请求。

参考:https://xz.aliyun.com/t/7501

https://www.anquanke.com/post/id/210036