Web缓存中毒漏洞笔记

概念

由非缓存键导致的差异化响应都能够被存储并提供给其他用户

储备知识

Web缓存中毒的目的是发送导致有危害响应的请求,该响应将保存在缓存中并提供给其他用户。

缓存通常通过CDN、负载均衡器或简单的方向代理来实现。

缓存键(cache key):通过缓存键来判断两个请求是否正在尝试加载相同的资源。

X-Forward-For:表示代理前的原始IP

x-Forwarded-For (XFF)在客户端访问服务器的过程中如果需要经过HTTP代理或者负载均衡服务器,可以被用来获取最初发起请求的客户端的IP地址,这个消息首部成为事实上的标准。在消息流从客户端流向服务器的过程中被拦截的情况下,服务器端的访问日志只能记录代理服务器或者负载均衡服务器的IP地址。如果想要获得最初发起请求的客户端的IP地址的话,那么X-Forwarded-For就派上了用场。

X-Forward-Host:表示原始的URL请求地址

**The X-Forwarded-Host **(XFH)是一个事实上的标准首部,用来确定客户端发起的请求中使用Host指定的初始域名。

X-Forward-proto/scheme:表示当前请求以http/https的方式

X-Forwarded-Proto (XFP) 是一个事实上的标准首部,用来确定客户端与代理服务器或者负载均衡服务器之间的连接所采用的传输协议(HTTP 或 HTTPS)。在服务器的访问日志中记录的是负载均衡服务器与服务器之间的连接所使用的传输协议,而非客户端与负载均衡服务器之间所使用的协议。为了确定客户端与负载均衡服务器之间所使用的协议, X-Forwarded-Proto 就派上了用场。

Via:代理服务器在转发时添加,作为标记。

Via 是一个通用首部,是由代理服务器添加的,适用于正向和反向代理,在请求和响应首部中均可出现。这个消息首部可以用来追踪消息转发情况,防止循环请求,以及识别在请求或响应传递链中消息发送者对于协议的支持能力。

Vary:赋予的值代表缓存键

Vary 是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。它被服务器用来表明在 content negotiation algorithm(内容协商算法)中选择一个资源代表的时候应该使用哪些头部信息(headers).

Vary * 表示请求被视为唯一并且非缓存的。这种情况更加建议使用Cache-Control:no-store来实现。

Cache-Control:缓存机制,当值为no-store时表示缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。当值为no-cache时表示每次有请求发出时,缓存会将此请求发到服务器(译者注:该请求应该会带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过期,若未过期(注:实际就是返回304),则缓存才使用本地缓存副本。当值为public时表示该响应可以被任何中间人(译者注:比如中间代理、CDN等)缓存。而 private则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。max-age=<seconds>表示资源能够被缓存(保持新鲜)的最大时间。

X-Original-URL/X-Rewrite-URL:对这些报头的支持允许用户通过X-Original-URLX-Rewrite-URL HTTP请求报头重写请求URL中的路径,并允许用户访问一个URL,但web应用程序返回一个不同的URL,这可以绕过对更高级别缓存和web服务器的限制。

漏洞利用过程

1.判断哪些非缓存键会影响页面内容

任何的缓存投毒都依赖于非缓存键,所以我们在一开始就要判断哪些HTTP头部属于缓存键,哪些不属于。再通过修改或添加HTTP头部来判断哪些头部会引起页面内容的变化。常用的两种方式:

  • 手动修改或添加HTTP头部,指定随机字符来判断头部是否影响页面内容
  • 使用Brupsuite插件Param Miner来自动判断,在burpsuite的URL右键选择Guess headers

注:在判断非缓存键的时候,可能会在无意间导致响应缓存下来,这时有其他用户访问就会收到刚刚我们测试时的缓存。所以我们在手动测试的时候可以手动添加一个特定的缓存键,比如请求https://vulnerable.com/?abc;如果使用的是burpsuite插件Param Miner则会自动添加随机值。

2.构造内容引起服务器端的有害响应

针对不同的非缓存键,我们需要知道哪些非缓存键会导致页面返回有害的内容。举一个例子:页面中js链接的域名是通过获取HTTP头部中的“X-Forwarded-Host”字段来设置的。而服务器不会将这个字段作为缓存键,那么这个字段就可以利用。

3.获取响应,使有害内容被缓存

通过构造有害的内容,访问页面,获取响应。就会将有害的内容存入缓存中。

缓存设计缺陷案例

1. X-forwarded-Host

如果网站以不安全的方式处理非缓存键的输入并允许后续的HTTP响应被缓存,则他们很容易遭受Web缓存中毒。

比如

GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: innocent-website.co.uk

HTTP/1.1 200 OK
Cache-Control: public
<meta property="og:image" content="https://innocent-website.co.uk/cms/social.png" />

x-forwarded-host头的值用于动态生成image的URL,以上的案例可以这样利用:

GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"

HTTP/1.1 200 OK
Cache-Control: public
<meta property="og:image" content="https://a."><script>alert(1)</script>"/cms/social.png" />

如果缓存了此响应,则将向/en?region=uk访问的所有用户都会收到XSS影响。

Cookie有时也用于在响应中动态生成内容,如果cookie也存在非缓存键则也会收到影响。

3. X-Forwarded-scheme/X-forwarded-Proto

X-Forwarded-scheme/X-Forwarded-Proto头:当值不为https时,表示当前请求以http的方式发送,一般情况下都会返回302跳转到当前URL的https协议请求。当非缓存键是X-Forwarded-scheme头时,如果网站同时支持X-Forwarded-Host则可以通过两者结合达到web投毒的攻击效果。

GET /random HTTP/1.1
Host: innocent-site.com
X-Forwarded-Proto: http

HTTP/1.1 301 moved permanently
Location: https://innocent-site.com/random

4. 返回缓存信息头

暴露太多的响应信息也可能会让攻击更容易。

HTTP/1.1 200 OK
Via: 1.1 varnish-v4
Age: 174
Cache-Control: public, max-age=1800

这里暴露出了缓存的机制和时间,攻击者可以根据此时间来操作。不用大量的重放攻击。

e. Vary头一般情况下值为User-Agent,表示UA也作为缓存键,根据这个,我们可以通过Web缓存攻击特定的UA用户。

f. 有时Web会使用Json传参,并通过JavaScript操作数据,这种情况下就有可能导致基于DOM的XSS问题。

我们需要操作的是让我们攻击服务器上的恶意Json文件投毒到缓存,注意⚠️:如果使用Web缓存中毒使网站从服务器加载恶意Json数据,则需要使用CORS授予网站访问JSON的权限,像下面一样。

HTTP/1.1 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: *

{
    "malicious json" : "malicious json"
}

缓存实现缺陷案例

探测缓存实现缺陷的方法与经典的web缓存中毒方法略有不同。这些较新的技术依赖于缓存的特定实现和配置中的缺陷,这些缺陷可能因站点而异。

1.Host头忽略端口

缓存机制在判断缓存键时存在缺陷,比如缓存在解析Host头时只取了主机部分,直接忽略掉了端口部分,所以可以设置特殊的端口来投毒。

2.查询字符串为非缓存键

GET /?a=1'><script>alert(1)</script> HTTP/1.1
Host: acc81f031e21840280fb449d0046000f.web-security-academy.net

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: close
Cache-Control: max-age=35
Age: 13
X-Cache-Key: /$$
X-Cache: hit

通过X-Cache-Key: /$$可以知道?a=1'><script>alert(1)</script>未被识别成缓存键,这样就成功投毒了

3.缓存参数伪装

GET /?example=123?excluded_param=bad-stuff-here

上面能成功投毒的前提有:

  • 缓存将上面识别成两个参数,并且缓存键会排出掉第二个参数
  • Web服务器不接受第二个?为参数分隔符,将其全部认为是一个参数

这样就可以将第二个参数的恶意代码投毒成功了。

4.利用参数解析怪癖(Ruby on Rails框架)

Ruby on Rails框架将与符号(&)和分号(;)都解释为定界符

GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here

缓存将请求解析为两个参数,并且会从缓存键去除第二个:

  • keyed_param=abc

  • excluded_param=123;keyed_param=bad-stuff-here

然而Ruby on Rails将请求解析为三个参数:

  • keyed_param=abc
  • excluded_param=123
  • keyed_param=bad-stuff-here

keyed_param参数出现了重复,在框架Ruby on Rails中这种情况会取最后出现的值,这样就成功的将恶意代码投毒了。

比如:

GET /jsonp?callback=innocentFunction&excluded_param=123;callback=alert(1)

5.支持胖GET方法

GET /?param=innocent HTTP/1.1
…
param=bad-stuff-here

在这种情况下(比较少见),缓存键将基于请求行,但是参数的服务器端值将从正文中获取。

如果网站不支持带body的GET,可以尝试使用请求头:X-HTTP-Method-Override来告诉服务器使用POST覆盖GET

6.规范化的缓存键

一些缓存机制会在添加缓存键时实现规范化,意思就是以下两种请求将视为同一个缓存键

GET /example?param="><test>
GET /example?param=%22%3e%3ctest%3e

正常我们在浏览器提交URL时,浏览器会自动encode。

使用Burp Repeater发送未经encode的恶意请求来毒害缓存,当受害者通过浏览器发出被encode的请求时就会响应被毒害的缓存。

7.缓存键注入

请求行和Origin头都是缓存键,发现Origin的值会影响响应的值,这种情况下我们可以使用缓存键注入的方式来达到投毒的效果。

GET /path?param=123 HTTP/1.1
Origin: '-alert(1)-'__

HTTP/1.1 200 OK
X-Cache-Key: /path?param=123$$Origin='-alert(1)-'$$

<script>…'-alert(1)-'…</script>

然后诱使受害用户访问以下URL,则会向他们提供中毒的响应:

GET /path?param=123$$Origin='-alert(1)-'$$ HTTP/1.1

HTTP/1.1 200 OK
X-Cache-Key: /path?param=123$$Origin='-alert(1)-'$$
X-Cache: hit

<script>…'-alert(1)-'…</script>

posted on 2021-05-18 11:31  tech_lee  阅读(1384)  评论(0编辑  收藏  举报

导航