前言
偶然看到的这个攻击方式,感觉在日常的挖洞环境中能够遇见的,故学习一波加深理解.
HTTP Request Smuggling最早是由Watchfire提出的,但是利用条件比较困难所以一直就被忽略了。
形成原因
自从HTTP/1.1以来,广泛支持通过单个底层TCP或SSL/TLS套接字发送多个HTTP请求。该协议只需将HTTP请求简单的拼接在一起,然后服务通过解析HTTP头部来确定每个请求的结束位置和下一个请求开始的位置。
现代Web系统通常会使用反向代理服务器来负责转发用户以及源站服务器的流量,这样,为节省建立连接的时间,通常会重用反向代理服务器与源站服务器的TCP连接,也就是说所有用户的请求流量都通过同一通道被源站服务器处理:
这就严格要求源站服务器在处理请求的时候,需要和反代识别的请求位置一致.倘若恶意用户使用的恶意Payload被源站服务器解释成两个不同的HTTP请求,而后者就会去影响正常用户的请求流量:
这就导致正常用户的请求被污染,返回一个非预期的响应,完成了一次走私攻击
攻击手法
1.CL-CL
If a message is received without Transfer-Encoding and with either multiple Content-Length header fields having differing field-values or a single Content-Length header field having an invalid value, then the message framing is invalid and the recipient MUST treat it as an unrecoverable error. If this is a request message, the server MUST respond with a 400 (Bad Request) status code and then close the connection.
根据 RFC7230
标准,规定当服务器收到的消息有多个Contnet-Length
值或单个Content-Length
值但属于不合法的值,必须返回400错误并且关闭连接
我们假设这样的一种情景,拿到这个数据包时,反代和源站服务器均不返回400错误,但反代按照第一个CL值进行处理,而源站服务器却按照第二个CL值进行处理:
1 | POST / HTTP/1.1\r\n |
反代获取到的数据包长度为8,是整个数据包的长度,所以将其原封不动的转发给源站服务器.而后者获取到的数据包长度为7,也就是截止到\r\n
就认为读取完成并处理完成后返回响应包.此时的a
就被遗忘在缓冲区中,假如此时有个正常的请求包被转发至源站服务器:
1 | GET /index.html HTTP/1.1\r\n |
这个请求就被源站服务器理解为:
1 | aGET /index.html HTTP/1.1\r\n |
处理完之后就会返回一个aGET request method not found
的错误.这样就完成了一次走私攻击
2.CL-TE
Transfer-Encoding
HTTP/1.1规定所有的HTTP连接都必须是持久的,除非设置Connection: close
,对于这种持续连接,浏览器想要判断响应实体是否结束,于是乎引入了Content-Length
头部来告知浏览器,后者可以根据值来判断响应实体的结束.但是对于动态生成的内容来说,得到准确长度是比较困难的,需要额外加大内存开销和等待时间.于是乎引入了Transfer-Encoding
字段
Transfer-Encoding: chunked
在这个分块传输功能内,只要可能,产生的数据就立刻作为部分内容先发送出去.单独传输的每部分长度都已16进制整数标识并放在一个单独的行里(不包括CRLF),但整个文件的长度是不确定的,直到出现最后一个0字节长度标志着整段传输完毕
—-Web之困
一个分块响应形式如下:
1 | HTTP/1.1 200 OK |
If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.
根据 RFC7230
标准,如果同时收到Transfer-Encoding
和Content-Length
,后者必须忽略.但是并没有说返回错误关闭连接,所以这里就可能存在问题
原理
同时存在两个头部时,反代服务器处理Content-Length
,而源站服务器处理Transfer-Encoding
同样我们假设这样的情景,拿到这个数据包,反代服务器识别CL然后将整个数据包转发给源站服务器
1 | POST / HTTP/1.1\r\n |
请求包到达源站服务器后,同时存在CL和TE头部,服务器按照标准忽略CL头部.当读取到0/r/n
时,认为读取完成,同理接下来的正常请求就会被污染
1 | aGET /index.html HTTP/1.1\r\n |
返回Unrecognized method AGET
报错
实验环境
构造恶意数据包:
紧接着正常访问URL:
返回403报错
3.TE-CL
与上一个CL-TE相反,反代服务器处理Transfer-Encoding
,而源站服务器处理Content-Length
1 | POST / HTTP/1.1 |
反代服务器处理TE头,所以读取到0\r\n
时,认为读取完了.然后转发给源站服务器.
源站服务器根据CL值处理数据,当读到12\r\n
的时候,认为读取完成,同理接下来的正常请求就会被污染
1 | APOST / HTTP/1.1\r\n |
返回Unrecognized method APOST
报错
实验环境:传送门
构造恶意数据包:
紧接着正常访问URL:
返回403报错
4.TE-TE
反代服务器和源站服务器均处理Transfer-Encoding
,但是对TE进行某种混淆操作导致其中某个服务器不处理请求TE头部,所以说还是变种TE-CL或CL-TE
常用的畸形方式:
1 | Transfer-Encoding: xchunked |
然后根据TE-CL或者CL-TE构造数据包。
漏洞环境: 传送门
构造数据包:
1 | Content-Length: 4 |
然后发送正常的GET请求
可以发现被污染了。其实就是TE-CL攻击
RoarCTF2019-Web:easy_calc
复盘环境:传送门
打开之后是一个简单的计算器,随便输入一个计算式,返回结果2
查看网页源码,有个小提示
<!--I've set up WAF to ensure security.-->
拿到form表单提交地址:/calc.php
,直接访问拿到php源码
1 |
|
直接访问/calc.php?num=phpinfo();
会返回403。需要绕过WAF,试了下CL-CL请求走私
虽然导致了400报错,但是前端服务器把数据包都转发给了后端并执行了
现在需要构造Payload去getflag,但是num参数中不允许出现空格、制表符、换行、单双引号、异或等符号
这里可以用base_convert()
进行Bypass。
base_convert ( string $number , int $frombase , int $tobase ) : string
返回一字符串,包含 number 以 tobase 进制的表示。number 本身的进制由 frombase 指定。frombase 和 tobase 都只能在 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
scandir => base_convert(61693386291,10,36)
/ => hex2bin(dechex(47))
payload1:
1 | var_dump(base_convert(61693386291,10,36)(hex2bin(dechex(47)))); |
readfile => base_convert(2146934604002,10,36)
payload2:
1 | var_dump(base_convert(2146934604002,10,36)(hex2bin(dechex(47)).f1agg)); |
同理,CL-TE也能够成功执行
拓展攻击
Upgrading XSS
在审计一个SaaS应用程序时,Param Miner7发现了一个名为saml的参数,Burp scaner证实它易受反射XSS的攻击。反射式XSS本身不错,但在规模上很难利用,因为它需要用户交互。
通过请求走私,我们可以对主动浏览网站的随机用户提供包含XSS的响应,从而实现直接的大规模利用。我们还可以访问authentication headers 和仅HTTP cookie,这可能会让我们转到其他域
Grasping the DOM
在www.redhat.com上查找请求走私链的漏洞时,我发现了一个基于DOM的开放重定向,这带来了一个有趣的挑战
页面上的一些javascript正在从受害者浏览器的查询字符串中读取“redir”参数,但我如何控制它?请求走私使我们能够控制服务器认为查询字符串是什么,但是受害者的浏览器对查询字符串的认知只是了解用户试图访问哪个页面。
我可以通过服务器端的某个重定向来解决这个问题:
受害者浏览器将收到一个301重定向到https://www.redhat.com/assets/x.html?redir=//redat.com@evil.net/,然后执行基于dom的开放重定向并将其带着数据跳转到evil.net上。
Reference
https://www.smi1e.top/%E5%9B%BD%E8%B5%9Blove_math%E9%A2%98%E8%A7%A3/
https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn