前言

老规矩,还是要简单介绍一下这个技术,实际上这个技术和我之前总结的HTTP Smuggling一样,属于比较”冷门”的漏洞,在大家热衷于寻找RCE、XSS、Sql injection的今天,此漏洞看似“垂垂老矣”,其复杂的攻击方式及“理论上的”威胁实在难以得到安全研究人员的青睐

但是实际上,还是老话,没有无用的,只有你不会的~

简单说,Web Cache Posioning旨在欺骗网站缓存,尝试劫持缓存服务器,使其缓存错误的资源,从而导致其后一系列如:DOS,XSS等等的攻击

那我们就来详细聊一聊这个“孤独”的攻击手法吧

原理

由于Web Cache Posioning这种攻击手段与缓存有着很大的关联,所以首先,需要先熟悉一下缓存的基本原理

下图可以简单了解缓存的原理:

cache

简单来说,就是当一个用户访问了例如/static/test.png这一资源后,Cache服务将会缓存(如果是可缓存的话)这一资源,当之后再有用户访问相同的资源时,Cache服务将不会转发此请求至后端服务器,而是直接将其保存的对应的数据返回。

听起来是不是很简单?这也是我们经常使用的CDN的最为基本的原理

但是,作为一个安全人员,在上面这句话中,总也能嗅到一丝危险的味道,“不会转发”、“直接返回”这样的字眼往往蕴含着巨大的风险

而Web Cache Posioning就是基于这些字眼而发起的攻击,我们的目标就是欺骗前端的缓存服务器,让其误认为受害者与攻击者访问的是同一个资源,从而在不请示后端服务器的情况下,误把被攻击者污染后的资源文件提供给受害者。

cache-posioning

对于缓存服务器,如果其判断是否是同一个资源的规则是基于url以及host头时,那么缓存服务器将简单地认为下两个请求是等同的

first-request

second-request

可以看到,橙色字体将是缓存服务器判断的根据,所以缓存服务器将会把第一个请求的响应拿来作为第二个请求的响应

但是我们可以看到,蓝色字体部分,两个请求所请求的文本语言是不一样的,故而这样的话,缓存服务器就“错误”地为第二个用户提供了错误语言版本的响应

在某种程度上,这就是最简单的(也是不太可能出现的)“欺骗”

但,事情从此才开始有趣了起来(笑)

手法

那么问题来了,我们应当如何有效的“欺骗”缓存服务器呢?

首先,我之前所讨论的HTTP Smuggling技术,其实就是可以完成这一操作的,如果想不明白,可以回过头看看那篇文章

当然了,我不会重复讨论同样的手法,所以这里要讲的,是另一种,更为有效,也更为直接的手法,即利用HTTP包头的解释歧义来进行缓存毒化

详细看看吧

基础入门

这里将会举一个redhat.com的真实例子

在redhat.com的一个页面上,我们发现了一个特殊的标头,如下图所示:

redhat-before

可以看见如果我们在请求中带入X-Forwarded-Host标头,那么此标头的值将被用来在meta标签里生成url,那么如果试试xss呢?

redhat-after

可以发现,并未做任何过滤,我们的X-Forwarded-Host的值直接被带入了meta标签,造成了XSS

那么,这个界面是否会被CDN缓存呢?

这时我们打开无痕浏览,模拟受害者,再次访问这个页面

redhat-result

可以看见,我们成功的让CDN缓存了这个界面,所有访问这一页面的用户将会在不知情的情况下遭受XSS攻击

变得更为谨慎

上面我们成功的毒化了https://www.redhat.com/en?dontpoisoneveryone=1, 但是,当我们想要毒化类似https://www.redhat.com/en 这样的链接时,我们将会遇到很多困难,最主要就是由于这种链接的访问流量将会很大,攻击者很难保证自己的请求能在缓存过期时精确的成为第一个到达的请求,而光靠暴力的方式往往会消耗大量的资源却无法得到预期的结果

所以,我们需要抓住一切我们可以利用的细节,尝试缩小我们尝试毒化的时间窗口

就像在unity3d.com中,我们可以发现如下的场景

unity3d

从图中可以看到,可以使用X-Host标头向网页中注入数据

另外,可以看见在响应中包含有Age和Cache-Control标头,其中Age标头代表此缓存已经被记录了多少秒,而Cache-control标头表示了过期时间,这里是1800秒

基于以上的信息,攻击者就可以大大缩小尝试攻击的时间窗口,降低时间成本

当然了,很多时候大部分网站并没有unity3d这么“友善”,这也是此类攻击技术难以大规模应用的一个原因

精准投放

当缓存服务器将UA作为缓存键的一部分时,我们就可以实现对某一类用户的精准毒化

vary

从图中可以看到,服务器的响应中包含了vary头,vary头代表了服务器将会以何键作为判断的标准,在这里,UA以及AE头即是判断的标准

所以,我们便可以对某类UA头进行毒化,实现精准投放

DOM毒化

当然了,也不是所有的毒化都像第一个例子一样那么轻松惬意地植入XSS的payload,又或者说,缓存服务器会有一些“额外”的特殊行动

就比如如下这种

dom1

可以看到我们可以利用X-Forwarded-Host头注入一个自定义的域名

然后我们发现,此页面调用了data.host用来加载一个外部json

dom2

json包含的内容如下所示

dom3

可以看见json内容包含了网页翻译文本,那也就是说,我们可以控制网页翻译的文本,利用缓存毒化感染使用特定语言的用户

但我们在json返回的文本中可以看见,由于默认的语言是英语,故而英语不提供翻译文本,也就是说我们无法毒化使用英语的用户

所以我们需要找一个办法来强制使用英语的用户转变语言至我们控制的语言(这里使用es)

尝试更换语言至es,观察到浏览器发出了GET /setlang/es请求,响应如下

dom4

可以发现,首先这个302是可以被缓存的,并且跳转后的页面路径是/?localized=1,那么就要考虑如何使得使用英语的用户在访问主页的时候“被迫”更换语言

通过对GET / 请求的分析,发现了我们可以使用X-Original-URL来重新指定访问的路径

dom5

但是我们可以发现,这个请求无法被缓存,这样就无法将其传递给其他的用户

通过尝试,发现如果将X-Original-URL: /setlang/es转为X-Original-URL: /setlang\es,服务器将在302请求中将\转为正确的/,并且这样的请求将可以被服务器缓存

dom6

那么现在,利用链已经完整了,首先毒化 /?localized=1,使其访问我们所制造的恶意的json文件,并且在 /?localized=1被毒化的时间内,毒化 / 页面,强制任何访问此页面的用户强制跳转至使用特定语言的 /?localized=1 页面,并执行我们所植入的恶意XSS payload。

成功图如下

dom7

成功地执行了alert(document.cookie) 的payload

路由中毒

除了以上的案列,有些服务器还会有不少令人匪夷所思的操作

比如下面这个,其会被恶意的http头欺骗,并使用此恶意的http标头来请求内部的路由

route1

可以看见X-Forwarded-Server标头将会有比Host标头更高的优先级,这欺骗了服务器,让其请求了非预期的资源。但是返回的响应由HTML编码,无法直接XSS

所以如果要利用这个漏洞,我们需要在HubSpot上托管一个自己的界面,并放入恶意的payload

route2

此类问题在SaaS程序上极为常见,因为他们往往会由一个系统来处理去往不同服务的请求

标头“合作”

有时候,往往一个欺骗性的标头并不一定能够展现出效果,这时候,就要尝试使用多个标头“合作”欺骗

来看看下面的实例

more1

可以看到X-Forwarded-Host头覆盖了cookie上的domain信息,但是这貌似不会带来任何的脆弱问题,仅仅只是修改了cookie的一个无关紧要的参数并不会带来任何的安全风险

这时候,试着尝试更多的标头,最终找到了另一个非预期的标头X-Forwarded-Scheme

more2

看起来貌似也没啥用,仍然没有任何的威胁

但是,但我们把两者结合起来,就会发生奇妙的化学反应,产生如下的结果

more3

从而我们就获得了劫持任意请求至任意域的能力

劫持Open Graph

Open Graph是由fb创建的协议,我们在分享一个链接的时候,只要分享的网站支持OG,那么就可以提供更详尽的视图,增强分享的效果

在下面的这个站点上,我们使用欺骗性的标头将会影响到OG的url

og1

可以看到我们影响到了OG的url部分,但是仔细查看响应可以发现,Cache-Control标头的值为private,并且在多次尝试后也证实了服务器拒绝缓存此响应

但是在其他的页面上,却仍然使用了缓存

og2

在这里需要注意,如果按照这样的请求,你会发现这个缓存将无法被有效的缓存,详情可以查看 CloudFlare缓存机制

所以我们需要使用如下的请求

og3

此时就可以使得此响应被服务器缓存

这样就导致了所有在此页面上使用分享功能的用户最终都会分享攻击者所指定的地址

本地路由中毒

除了X-Forwarded-For等标头以外,实际上还有许多神奇的标头可以实现缓存毒化

就比如我之前使用到的X-Original-URL,其和X-Rewrite-URL一样,都可以“强制”转变请求路径,且这一路径的转变是缓存服务器所“看不见”的

就拿Drupal框架来说,以下两个请求将是等价的,但是很明显,服务器对此的响应却是完全不同

drupal1

drupal2

那么,我们就可以利用这一特性来欺骗应用程序提供完全超出预期的页面,比如下图

local-route1

如果我们发送这样的请求,那么对于前端缓存服务器来说,其所看到的是/education?x=y,但是实际后端服务器解析的访问路径却是/gambling?x=y

这样就会使得每一个访问/education?x=y路径的用户意外的得到/gambling?x=y的页面

当然,除了对于路径的毒化之外,缓存也有可能被毒化查询参数

local-route2

这样的话,我们将有机会诱导受害者查询非预期的结果

另外,如果遇到利用重定向来导入js文件的网站,如果存在缓存毒化漏洞,那么攻击者就有可能劫持js文件的加载,强迫受害者加载外部恶意js文件

Drupal开放式重定向

首先,Drupal框架存在这样的一个问题,即如下的请求将会导致Drupal框架错误的将用户导向任意一个外部网站

drupal-or

那么,配合缓存毒化漏洞,将会有意想不到的结果

首先我们先发送如下请求,毒化Drupal的内置缓存

drupal-posion-internal

这样,drupal在接到访问/redir路径的请求时,将会直接返回指向外部恶意网站的302重定向响应

接着我们毒化外部缓存

drupal-posion-external

此时,外部任意用户访问/download路径都将会被后端的Drupal缓存认为在访问/redir,从而返回恶意的302响应,并被外部缓存服务器所缓存

这样,我们就可以基于此向受害者下发恶意软件,导致极大的风险

一个请求瘫痪网站

听起来貌似不太可能,但是如果目标网站有着缓存毒化漏洞,那么这是完全可能的

假想以下一个请求

1
2
3
4
5
GET /index?v=test HTTP/1.1
Host: example.com

HTTP/1.1 301 Moved Permanently
Location: /index/?v=test

再尝试发一个请求

1
2
3
4
5
GET /index HTTP/1.1
Host: example.com

HTTP/1.1 301 Moved Permanently
Location: /index/?v=test

我们可以看到,服务器将301缓存了起来,但是由于我们只能污染查询参数,所以在这看来并没有什么用处

但是,仔细想想,你会发现实际上我们可以利用一个很少见的http状态码—414

我们发送以下的请求,将url填充至最大字节,且假设响应被缓存

1
2
3
4
5
GET /index?v=<long-string> HTTP/1.1
Host: example.com

HTTP/1.1 301 Moved Permanently
Location: /index/?v=<long-string>

那么当有人访问index时,会得到如下响应

1
2
3
4
5
GET /index HTTP/1.1
Host: example.com

HTTP/1.1 301 Moved Permanently
Location: /index/?v=<long-string>

那么这时,你会发现在重定向时,301响应为我们补全了index后的/符号

这就导致了如果受害者的浏览器收到这样的重定向响应后,由于本身url已经是最大值,而此时301响应多补了一个/,这就导致了url过长,使得任何一个用户访问index,都会得到一个HTTP 414响应

1
2
3
4
5
GET /index/?v=<long-string> HTTP/1.1
Host: example.com

HTTP/1.1 414 Request-URI Too Large
CF-Cache-Status: MISS

这就造成了拒绝服务攻击,而且这仅仅需要一个请求

后记

没啥好说的,骚就完事了