Chrome的AppCache漏洞小结 - Mads

admin 2021年12月31日15:46:00评论48 views字数 5393阅读17分58秒阅读模式

AppCache(Application Cache)是HTML5提供的应用缓存机制的一组接口,在各大浏览器(曾经)都有实现。它通过用户指定的manifest缓存特定页面从而使这些页面即使在离线状态的都可以访问,提高了访问速度且减轻了服务器的压力。

但随着技术发展,像Service Worker这种新的客户端缓存技术逐渐有了取代AppCache这种老大哥技术的趋势。AppCache由于其技术实现的缺陷,也被安全研究人员发现了一些漏洞。本文针对Chrome下的AppCache实现,回顾曾经被爆出来的安全漏洞

#1 AppCache Poisioning

这个漏洞在[1]和[2]中被提及,主要的成因是AppCache的FALLBACK字段没有限制源URI的作用域。意思就是说即使attack.html和manifest.txt在网站的/1337/a/b/c/下,attach.html安装的manifest.txt也可以指定根目录/index.html的FALLBACK。这一缺陷结合Cookie Bomb等技术,只要使被劫持的页面返回500等错误就可以触发FALLBACK,使之被劫持到攻击者的恶意页面。一个例子如下:

https://example.com/1337/a/b/c/attack.html

<html manifest="manifest.txt">
  <script>
    for(var i = 1e2; ;i--)
      document.cookie = i + '=' + Array(4e3).join(0) + '; path=/';
  </script>
</html>

https://example.com/1337/a/b/c/manifest.txt

CACHE MANIFEST

manifest.txt

FALLBACK:
/ /1337/a/b/c/poison.html

当用户先访问了/1337/a/b/c/attack.html之后,再访问根路由/由于Cookie Bomb就会发生500错误,浏览器就会将/1337/a/b/c/poison.html页面的内容返回给用户。

这个漏洞的利用条件需要

  • 上传点,可以上传html和manifest
  • (或者说,返回内容可控的路由,比如html注入,可以注入<html manifest="">标签即可。

真实的案例可以是一些托管用户文件的网站,比如aws bucket、dropbox等等。

#2 XSLeaks

  1. Leak URL是否跳转

AppCache的CACHE字段可以设置跨域的URL,当设置的URL在请求时发生了跳转,appcache无法成功cache,onerror事件被触发,而当成功cache时,oncached事件被触发,这就可以oracle出一个跨域URL是否发生了跳转。比如下面的例子:

attack.html

<html manifest="manifest.appcache">
    <script>
        applicationCache.onerror  = () => console.log("User isn't logged since there was a redirect");
        applicationCache.oncached = () => console.log("User is logged since there wasn't a redirect");
    </script>
</html>

manifest.appcache

CACHE MANIFEST
https://www.facebook.com/settings

attack.html页面就可以探测出用户的facebook是否是登录状态,因为非登录状态访问https://www.facebook.com/settings会发生跳转。

  1. Leak 会发生跳转的URL内容

AppCache的NETWORK字段也可以设置跨域的URL,且是以白名单的方式允许哪些URL可以访问。

名词说明:

victim URL : 会发生跳转的URL

redirect URL :跳转之后的URL

如果将victim URL以及跳转后的redirect URL设置为NETWORK字段内容,则访问victim URL会正常跳转不会被拦截。而如果将victim URL和与redirect URL不一致的URL设置为NETWORK字段,则由于跳转后的redirect URL不在白名单内,所以访问会失败。博客里的例子如下:

cache.manifest

CACHE MANIFEST

NETWORK:
https://www.facebook.com/me
https://www.facebook.com/victim

attack.html

<html manifest="cache.manifest">
    <script>
        applicationCache.oncached = () => {
            fetch("https://www.facebook.com/me", {
                mode: "no-cors",
                credentials: "include"
            }).then(() => {
                console.log("The profile of the user is /victim");
            }).catch(()= > {
                console.log("The profile of the user isn't /victim");
            });
        }
    </script>
</html>

其中受害者的facebook在登录状态,当访问https://www.facebook.com/me时候,会自动跳转到https://www.facebook.com/<用户名>。于是当受害者访问attack.html时,页面就会oracle出受害者的facebook用户名是否是victim

这样我们可以把用户名顺序遍历的结果写到一个manifest中,利用二分法搜索出最终的用户名,相当于爆搜来说已经是很大的提升了,但是还是不够优雅。libherrera在看了AppCache的chrome源码发现了一种非标准的feature,叫做URL patterns。样子长下面:

CACHE MANIFEST

NETWORK:
https://www.facebook.com/me
https://www.facebook.com/vi*tim isPattern

于是通过URL pattern,就可以逐字符oracle出完整的用户名。

当然故事没有结束,在报告了这个漏洞之后的一年,作者又发现了manifest中的prefix match,更简洁的payload出来了,长下面的样子

CACHE MANIFEST

NETWORK:
https://facebook.com/me
https://facebook.com/v

两个bug分别是CVE-2020-6399CVE-2021-21168

这个漏洞的利用场景可以说是非常多了,比如:

  • 跳转后的URL包含session token
  • 跳转后的URL包含CSRF token

不再一一例举。

# 一道例题 Pwn2Win 2021 MessageKeeper

这道题的考点就是利用AppCache中的FALLBACK字段。

题目漏洞点就是有一个JSONP接口存在任意的html注入,但是CSP是default-src none

其中flag也在这个JSONP接口返回,但是需要这个JSONP接口的另一个参数token也正确才行。

所以题目基本就是需要拿到这个admin的token,然后用这个JSONP的功能就可以拿到flag。看看解法最后用到的manifest长啥样吧(来自terjanq,和官方解基本一样

CACHE MANIFEST
/?cached

FALLBACK:
/user?token=a /static/background.png

ORIGIN-TRIAL:
${trial_token}
#

其中trial_token可以去Chrome Origin Trials申请一个,值得一提的是可以申请任意origin的token,即使这个origin不属于你。

这个manifest的意思是缓存/?cache,其实也就是首页,加个参数可以保证不影响到访问/并且有限制FALLBACK作用域的作用。所以此时,下面的FALLBACK字段也只作用于/?cached页面。

首页中有脚本调用了这个JSONP,所以这个script标签的src会被作用于FALLBACK字段

但如何让这个FALLBACK生效呢?解法有一步是主动调用/logout让admin退出,就是为了让/user接口返回401,从而让FALLBACK起作用。

所以当这个appcache manifest注册之后,第一次访问/?cached,首页script的src向/user?token=<正确的token>发起请求返回401,如果:

  1. 此时攻击者注册的manifest中的FALLBACK字段的URI/user?token=a匹配了这个真的/user?token=<正确的token>,那么浏览器就会先请求/static/background.png然后返回图片的内容。而这个/static/background.png设置了Cache-Control: public, max-age=14400返回头,也就是说会缓存在本地中,下次访问直接从本地缓存拿。
  2. 此时攻击者注册的manifest中的FALLBACK字段的URI/user?token=a没有匹配到真的/user?token=<正确的token>,那么浏览器就不会去请求/static/background.png这个文件,而是直接报401。

这样当第二次再请求/?cached的时候,如果匹配正确,会直接从本地缓存拿/static/background.png文件,如果匹配失败,依然会发送一个http请求。

这样,两次请求的时间差异就会因为匹配成功与否呈现出差别,以此就可以进行oracle,结合prefix match就可以逐字节oracle出admin的token。

上面的解释应该是题目作者gist上的意思,但是他的gist代码感觉并不是这个意思,因为gist的payload只对/?cached进行了一次load,并不是两次。

所以我测试了一下FALLBACK的机制,也就是FALLBACK被触发一次之后,会不会直接cache触发FALLBACK的URI的返回结果。如果不会cache,那么作者的这种解释就显得牵强,因为两次请求触发FALLBACK的URI都会被浏览器请求一次然后再返回FALLBACK后的URI(触发FALLBACK的URI请求不会在console显示),这样能触发FALLBACK的URI反而会因为还要加载新的URI(即题目中的background.png)而花费更长的时间。

测试的结果也验证的我的猜想,FALLBACK并不会cache触发FALLBACK的URI的返回结果。图中可以看出从disk cache和401的两个结果花费的时间基本一致,因为disk cache看起来是从缓存拿的结果,实际上也进行了一次http request。

那么作者提供的payload为什么会起作用呢。在测试的时候我注意到一个有规律可以复现的现象,那就是chrome会retry被401的/user?token=xx请求

而被FALLBACK匹配的/user?token=xx请求则不会进行retry。而这多的一次retry的时间刚好和oracle的时间差吻合,到这里应该也就大概明白了这个oracle的逻辑了。

整理下这个题的关键点:

  1. FALLBACK的作用域可以被manifest限制在独立的URI里
  2. chrome会对失败的401请求进行retry,结合AppCache的FALLBACK会导致一个侧信道

Timeline

  • Chrome 50在非安全环境下“废弃” - 2016/4
  • Chrome 70在非安全环境下“移除” - 2018/10
  • Chrome 79在安全环境下“废弃” - 2019/12
  • Chrome 80限制AppCache作用域 - 2020/2
  • Chrome 84开启“reverse origin trial” - 2020/7
  • Chrome 85在安全环境下移除,但依然可以通过"reverse origin trial"开启 - 2020/8
  • Chrome 93完全移除 - 大约2021/10

Reference

BY:先知论坛

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月31日15:46:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Chrome的AppCache漏洞小结 - Madshttps://cn-sec.com/archives/712474.html

发表评论

匿名网友 填写信息