前言

实际上HTTP Smuggling已经不是一个很新的攻击技术,早在2005年,就已经有人提出过这一攻击方式,只是这一攻击方式普遍被认为过于复杂以及难以利用于实战,故而在被提出后并没有受到太多的重视

然而,往往人们不看好的东西会有着巨大的财富

借用albinowax的原话:

If a technique has a reputation for being difficult, fiddly, or dangerous, that’s a topic in dire need of further research. After repeatedly experiencing breakthroughs due to being pressured into exploring topics well outside my comfort zone, I’ve decided that the fastest route to novel findings is actively seeking out topics that make you uncomfortable. Chances are, these topics are avoided by other hackers, giving them serious research potential. To me, this is the only plausible explanation for why I was able to take a technique first documented in 2005, and presented again at DEF CON in 2016, and use it to earn $70k in bounties in 2019.

所以,好好研究,没有不好用的攻击技术,只有你不会玩的~

介绍

HTTP偷渡(HTTP Smuggling)的原理可以简单用下面两张图来解释:

首先是正常环境下的,用户访问一个带有反代服务器的网站:

normal

可以看到,由于HTTP / 1.1的兴起,反向代理服务器往往会将多个用户的请求通过单个的tcp链接串联在一起发送至后端服务器,实际上这一方法本身是无害的,只要前后端对于每一个消息的开始、结束位置达成一致,那么这一技术会大大减轻前后端服务器的负载,加快数据传输的效率。

但是,这也成为了一个薄弱的环节,因为这就意味着前后端必须对每个消息的结束位置达成一致,如果无法达成一致,那么就有可能发生如下图的情况:

smuggling

也就是说攻击者可以构造畸形模糊的请求,使得后端服务器在处理攻击者的请求时只处理了“部分”恶意请求,而把“剩余”的恶意请求以及正常的用户请求视作同一个请求并进行处理,从而劫持用户的请求。

这种攻击手法,就被称为HTTP偷渡(HTTP Smuggling)技术

基本原理

首先,现在的核心问题在于,我们应该如何构造一个所谓的畸形模糊的请求

这里就要引入一个我们大家都很熟知的概念—Transfer-Encoding

相信大多数人知道这个概念都是因为我们可以利用Transfer-Encoding绕过waf,但是,实际上这一技术带来了许多神奇的攻击面,包括今天讨论的主角HTTP Smuggling

我们大家都知道,一个HTTP POST请求当中往往会有一个Content-Length头,这个头标示了此POST请求所携带的数据的长度,而如果我们在一个HTTP请求当中同时添加两个Content-Length头,并且使用不同的值,绝大多数的服务器会直接拒绝这种格式的请求,认为此格式是非法的

但是,参考RFC 2616规范,如果同时存在Content-Length以及Transfer-Encoding头,文档规定Content-Length头需要被忽略。故而我们可以得知一点:如果一个post请求当中同时携带Content-Length以及Transfer-Encoding头,那么这个请求是合法的,绝大多数的服务器并不会拒绝这样的请求

那么问题就来了,如果两台服务器对这样的请求,一台服务器不支持Transfer-Encoding,另一台支持,那么,整个系统就不同步了,攻击者可以籍此触发HTTP Smuggling漏洞

以下两张图可以帮助理解此问题:

  • 首先我假设前端服务器并不支持Transfer-Encoding,而后端服务器支持,在HTTP Smuggling中,我们往往将这一情况称为CL.​​TE

CL.​​TE

在图中,我们可以看到,由于前端服务器不支持Transfer-Encoding,故而前端服务器只会转发蓝色的文本至后端服务器,而后端服务器由于一直无法收到以0结尾的Transfer-Encoding消息结束块,故而会一直等待,此链接就会超时。

  • 而如果前端服务器支持Transfer-Encoding,后端服务器不支持,那么在HTTP Smuggling中,这类情况被称为TE.CL

TE.CL

在图中,可以看到由于前端服务器支持Transfer-Encoding,故而前端只会转发蓝色字体部分至后端服务器,而此时由于后端服务器需要读取6个字节长度的数据,需要等待最后一个X的到来,故而此时链接就会超时

这两种方式都可以用来检测潜在的HTTP Smuggling风险

那么也许有人会说,这不过就是让自己的链接超时罢了,没有什么大用,如果是这么想的话,看看下图如何?

Attack

从图中可以看到,这是一个CL.​​TE类型的HTTP Smuggling攻击,前端服务器转发了包括蓝色及橙色消息在内的所有消息,而后端服务器在解析的过程中,只解析了蓝色字体部分,这就使得橙色消息被留在了缓冲区中,并与后来的一个受害者请求(绿色字体)合并,称为了一个新的HTTP请求

也就是说,原本受害者希望向/search路径POST一个请求,但是由于HTTP Smuggling的存在,后端服务器在用户完全不知情的情况下,误认为受害者向/404发出了一个get请求,从而引发了受害者无法访问/search路径的问题。

而这里的路径是完全可以自定义的,攻击者完全可以利用HTTP Smuggling漏洞迫使受害者加载恶意的外部js或者结合缓存漏洞形成拒绝服务攻击

攻击手段

上面我简单介绍了一些HTTP Smuggling的基础攻击手段及方式,接下来,我将更深入探讨HTTP Smuggling的更多用法

窃取“秘密的”HTTP标头

相信大家都知道,很多反向代理服务器会在代理HTTP请求至后端服务器的同时,附加一些HTTP头,比如我们所熟知的X-Real-IP等等

这些标头里往往包含有一些比较敏感的信息,因为有些网站会将认证相关的信息附在HTTP头中

HTTP Smuggling攻击将帮助我们获取这些敏感信息

首先,我们需要一个存在漏洞的网站(在此例中是CL-TE)并找到一个能够反射出我们输入的参数的端点,比如我们常用的搜索框:

search

可以看到,我搜索了“hello”后,上方的提示栏会将我们搜索的内容反射出来

既然如此,我就可以构造如下的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
POST / HTTP/1.1
Host: xxx
Connection: close
Content-Length: 125
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: https://ac701fe11e21cc0a80c70e6500f4004c.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: xxx
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8
Cookie: session=nMmCAchGAacq8iL9F1dNu70NqtFYpCfd
Transfer-Encoding: chunked

0

POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 200
Connection: close

search=hello

由于CL-TE型漏洞的存在,故而前端将会转发所有的请求字符,但是,后端服务器在解析时,却仅解析了终止符,故而以下的字符被留在了缓冲区中

1
2
3
4
5
6
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 200
Connection: close

search=hello

当我将第一个请求再次发送时,在后端服务器看来,将会变成这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 200
Connection: close

search=helloPOST / HTTP/1.1
Host: xxx
Unknown-header:xxxxxxxxx
Connection: close
Content-Length: 125
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: https://ac701fe11e21cc0a80c70e6500f4004c.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: xxx
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8
Cookie: session=nMmCAchGAacq8iL9F1dNu70NqtFYpCfd
Transfer-Encoding: chunked

最终可以看见这样的返回:

search-smuggling

可以看到,后端服务器误把第二个POST请求当作了HTTP头,返回并暴露了敏感的头信息

基于此信息,就有机会可以进一步利用内网的api等敏感基础设施

窃取身份验证信息

这个和上一个很像,基于同样的原理,我们可以窃取并保存用户的身份验证信息

发送如下的请求:

cookie

在此例中,服务器存在TE-CL漏洞,前端服务器将蓝色及橙色字体完整地转发给了后端服务器,而由于后端服务器不支持TE头,故而只能解析蓝色字体部分,橙色字体的部分便被留在了缓冲区中,等待受害者请求(绿色字体)到来时,橙色字体部分将和上一个例子一样,与绿色字体部分相结合,构成一个完整的HTTP请求,从而使得受害者本身的GET请求变为了一个更新个人信息的请求(这里的个人信息是攻击者的个人信息)

攻击成功后如下:

cookie-smuggling

可以看见,受害者的HTTP头信息被保存在了攻击者的bio中,只需要调节橙色部分的CL长度,就可以将受害者的所有HTTP头全部窃取

Self-XSS

相信大家在挖洞的过程中一定遇到过不少的self-xss,这类xss很难被利用,所以往往我们在遇到这类漏洞的时候,会选择直接忽略

然而,若网站存在HTTP Smuggling漏洞,那么,Self-XSS也将成为威力巨大的武器

如果一个网站存在HTTP Smuggling漏洞,且其SAML参数容易收到XSS攻击的影响,那么,发送如下的请求:

self-xss

解析过程如前两例所示,此时黑色字体将是受害者所获得的HTTP响应,已然被植入了XSS Payload

借助HTTP Smuggling,Self-XSS也将成为不可忽视的漏洞点

Open-Redirecting

同样的,由于我们可以实现Self-XSS,那么,在遇到一些基于DOM的开放重定向时,HTTP Smuggling也能发挥一波

例如在redhat.com上有一个页面有如下的代码:

1
2
3
4
5
6
7
8
9
GET /assets/idx?redir=//redhat.com@evil.net/ HTTP/1.1
Host: www.redhat.com

HTTP/1.1 200 OK

<script>
var destination = getQueryParam('redir')
[poor filtering]
document.location = destination

js尝试读取get请求中的redir的值并将网页重定向至其所指向的页面

此时发送如下的请求:

open-redirect

我们就可以劫持任意用户跳转至我们所希望的网页。

CDN

许多网站会使用CDN技术,并且CDN的提供商也往往提供基于HOST标头,从一个网站访问同一CDN网络上的另一个网站的服务

那么,我们可以对存在漏洞的网站发送如下的请求:

cdn

这样我们就可以将原本访问redacted.com的受害者引导至www.redhat.com网站

更进一步说,我们完全可以使用户加载一个错误且非预期的资源

SaaS提供商同样适用这一特性

“帮凶” Apache和IIS

相信大家应该都有注意到基于Apache以及IIS所搭建的网站都有一个这样的特性,就是当你访问一个不以反斜杠结尾的文件夹时,Apache和IIS都将会以HOST标头为基础重定向响应,以此来向文件夹末尾追加斜杠

比如说这样一个请求:

1
2
GET /test HTTP/1.1
Host: example.com

在发往由Apache以及IIS搭建的网站时,会收到如下的回复

1
2
HTTP/1.1 301 Moved Permanently
Location: https://example.com/test/

基于这个看来无害的特性,同时利用HTTP-Smuggling,我们可以使用如下的方式进行攻击:

apache-iis

如图就可以将受害者的请求导向我们所指定的网站,另外,如果目标网站回复的是307的状态码,则可以更进一步,窃取用户POST的敏感数据

因为307会使得POST请求重新发送至新的目标,但是需要用户的确认,RFC原文为: If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued

缓存毒化

缓存毒化是一个很有趣的话题,我会在之后的blog中详细讨论这个技术,现在,来瞅瞅这一技术如何与HTTP-Smuggling配合,实现惊人的效果

例如我们向目标网站发出如下请求:

cache-posion

受害者本身是访问/static/site.js的,然而却被我们劫持并访问了自己的账户信息,并且此时这个请求是携带受害者本身的cookie的

如果此网站存在缓存漏洞,那么在后端服务器返回账户信息的同时,缓存会错误的认为,/static/site.js的响应是受害者/account/settings所返回的信息并将其缓存

此时,作为攻击者,我们只需要访问/static/site.js这个静态文件,就可以获得受害者的账户信息:

account

攻击实例

接下来的攻击实例是基于albinowax大神对于paypal的攻击

发现请求走私漏洞

首先攻击者发现了paypal登录页面上存在请求走私的漏洞,基于此,攻击者可以劫持几乎所有的paypal登陆界面所引用的js文件,如下:

paypal-smuggling

由于paypal使用了CDN,基于我之前介绍的基于CDN的攻击,攻击者可以籍此劫持受害者的js文件至外部网站的恶意js文件

注意,这里location重定向指向了http网站,而paypal网站是https的,故而无法引入http资源,但是在Safari和IE上有特例,Safari拥有HSTS机制,如果这个转向的网站已经在safari的HSTS缓存中,那么http将自动升级为https;而IE则是可以完全绕过这一限制,在https中引入http资源

所以本次攻击只对Safari和IE用户生效!

解决CSP

但是,由于paypal使用了CSP策略,故而劫持失败了

paypal-csp

攻击者继续寻找,发现了登陆界面有一个iframe加载了一个c.paypal.com上的子页面,并且此子页面没有使用CSP,同时导入了我们投毒的js文件,这时候攻击者就获得了iframe的完全控制权

paypal-iframe

解决同源策略

但是,此时由于c.paypal.com与paypal.com不同源,同源策略限制了对paypal.com的DOM树的访问

故而,攻击者进一步查找,终于在paypal.com/us/gifts上找到了一个不使用CSP,且导入了投毒后的js文件的网页,而因为攻击者已经完全控制了iframe,故而攻击者可以将此iframe重定向至paypal.com/us/gifts并第三次触发投毒后的js文件导入,从而籍被污染的js文件来操作paypal.com/signin的界面,获取明文的用户名密码。

paypal-success

后续

paypal修复了这个漏洞,通过将akamai配置为拒绝Transfer-Encoding: chunked来修复此问题

但是albinowax随后发现,如果将TE头修改成下面这种形式,将可以绕过paypal的缓解措施:

1
2
Transfer-Encoding:
chunked

后记

通过学习albinowax大神的文章以及相关的知识,又掌握了一个小小的黑科技hh,其实没有什么没用的漏洞,完全是看利用者的水平,菜刀在高手的手里,也会变成精巧无比的手术刀。

另外,立个flag,速速把缓存毒化的blog也搞出来hh