什么是 HTTP 请求走私?
不同服务器请求内容的标准不同,对同一段tcp内容,前后端服务器获取到的http请求内容会有一定差异,而这个差异就造成了HTTP请求走私。
漏洞成因
HTTP规范提供了两种不同的方法来指定请求的结束位置 :Content-Length标头和Transfer-
Encoding标头,若同一请求前端与后端所使用的规范不同就可能导致解析差异,从而造成请求走私。一般服务器有三种解析方式:
CL.TE:前端服务器使用Content-Length标头,而后端服务器使用Transfer-Encoding标头。
TE.CL:前端服务器使用Transfer-Encoding标头,而后端服务器使用Content-Length标头。
TE.TE:前端服务器和后端服务器都支持Transfer-Encoding标头,但是可以通过对标头进行某种方式的混淆来诱导其中一台服务器不对其进行处理。
这里的解析错误主要取决于HTTP1.1中的两个特性: keep-alive与pipeline
Keep-Alive :是在 HTTP 请求中增加一个特殊的请求头 Connection: Keep-Alive,告诉服务器,接收完这次 HTTP
请求后,不要关闭 TCP 链接,后面对相同目标服务器的 HTTP 请求,重用这一个 TCP 链接,这样只需要进行一次 TCP
握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。这个特性在 HTTP1.1 中是默认开启的。
Pipeline(http管线化)
:http管线化是一项实现了多个http请求但不需要等待响应就能够写进同一个socket的技术,仅有http1.1规范支持http管线化。在这里,客户端可以像
流水线
一样发送自己的HTTP
请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。
正常HTTP请求:
若前置服务器和后端服务器在 HTTP 请求的边界划分上未达成一致:
当我们向代理服务器发送一个比较模糊的 HTTP 请求时,由于两者服务器的实现方式不同,可能代理服务器认为这是一个 HTTP
请求,然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,当该部分对正常用户的请求造成了影响之后,就实现了
HTTP 走私攻击。
HTTP 流水线
上边写到Pipeline(http管线)可以像流水线一样发送http请求,所以这里简单了解下HTTP流水线原理
执 行nc xxx.xxx.xxx.xxx 80,然后发送三个HTTP请求。
GET / HTTP/1.1
Host: localhost
GET / HTTP/1.1
Host: localhost
GET / HTTP/1.1
Host: localhost
在一个连接中发送3个HTTP请求,服务器会按序响应3个HTTP请求。可以使用这种办法来最小化请求之间的间隔时间。但由于手工录入可能较慢,可能无法实现我们的意图,所以也可以用命令来代替。
echo -ne "GET / HTTP/1.1\r\nHost: localhost\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc xxx.xxx.xxx.xxx 80
这里就可以结合请求走私,执行一些我们想要执行的操作。
五种攻击方式
CL不为0
所有不携带请求体的HTTP请求都有可能受此影响。前端代理服务器允许GET请求携带请求体;后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的Content-
Length头,不进行处理。这就有可能导致请求走私。
代码实例:
GET / HTTP/1.1
Host: Sentiment.com
Content-Length: 44
GET / secret HTTP/1.1
Host: Sentiment.com
这里要提一下长度的计算规则:
Content-Length 需要将请求主体中的 \r\n 所占的 2 字节计算在内,而块长度要忽略块内容末尾表示终止的 \r\n;
请求头与请求主体之间有一个空行,是规范要求的结构,并不计入 Content-Length。
所以这里的44=21+19+2*2(这两行后边的换行,即:(\r\n
)
攻击流程
前端:收到该请求,读取Content-Length,判断这是一个完整的请求。 后端:因为它不对Content-
Length进行处理,由于Pipeline的存在,后端服务器就认为这是收到了两个请求,分别是:
第一个: