Dance Dance Robots

2023-10-07 :: (smac89) from SunshineCTF2023

DDR

All the cool robots are playing Digital Dance Robots, a new rythmn game that… has absolutely no sound! Robots are just that good at these games… until they crash because they can’t count to 256. Can you beat the high score and earn a prize?

nc chal.2023.sunshinectf.games 23200

Explanation

The goal of this challenge was to respond with the a move that matches the arrow being displayed. The challenge starts with:

Welcome to DIGITAL DANCE ROBOTS!

       -- INSTRUCTIONS --       
 Use the WASD keys to input the 
 arrow that shows up on screen. 
 If you beat the high score of  
     255, you win a FLAG!     

   -- Press ENTER To Start --

Upon pressing ENTER, the user is continually presented with lines that looks like:

⇦⇧⇦⇧⇨⇨⇨⇨⇧⇦⇨⇧⇧⇧⇨⇦⇨⇦⇦⇨⇦⇦⇩⇦⇩⇨⇧⇨⇧⇨⇧⇩⇧⇦⇧⇦⇩⇨⇧⇩⇩⇩⇧⇧⇧⇩⇨⇨⇦⇦

And you have 1 second to respond with the correct output otherwise, you are met with:

You lose... better luck next time!
Score: <your score>

Solution

The solution to this problem is simply to have a way of matching the arrows to their corresponding letter:

IN OUT
a
w
s
d

We will also need to have a way to keep track of what is going on so that we can know when we get a flag. For this part, we make use of standard error to print “debug” information.

The following bash script does exactly that:

solver.sh

#!/bin/bash
echo
robo_move=0
resp=()
while read -t2 -N1 ch; do
    echo -n "$ch" | tr >&2 -d '\r'
    case $ch in
    "⇧")
        echo -n 'w'
        resp+=('w')
        robo_move=1
        ;;

    "⇩")
        echo -n 's'
        resp+=('s')
        robo_move=1
        ;;

    "⇨")
        echo -n 'd'
        resp+=('d')
        robo_move=1
        ;;

    "⇦")
        echo -n 'a'
        resp+=('a')
        robo_move=1
        ;;

    *)
        if [ $robo_move = '1' ]; then
            echo
            printf >&2 '%s' $'\n' ${resp[@]}
            resp=()
            robo_move=0
        fi
        ;;
    esac
done
echo

The script above simply reads from stdin and writes to stdout. However, how it captures the basic idea of the solution: Ignore everything except for the arrows, then print a new line after a line of input has been processed. Everything else is for the sake of debugging and being able to read the flag at the end.

The script needs to somehow connect to the game server, read from it, and send output back to it. For this, we use the following shell pipeline:

nc -l localhost 3999 | nc chal.2023.sunshinectf.games 23200 | ./solver.sh > >(nc localhost 3999)

Let’s break the down above:

  • nc -l localhost 3999 creates a local netcat server listening for connections on port 3999. Don’t think too much about this one for now.
  • ./solver.sh > >(nc localhost 3999) starts our script and redirects its output to the standard input of the server we created earlier
  • in the middle we have nc chal.2023.sunshinectf.games 23200 which receives input from the server running on port 3999, and sends output to our script (solver.sh).

The intermediate server could also be replaced with named pipes |.

Result

Finally, after ~5 mins, we get a nice:

YOU WIN!!!
Your prize is sun{d0_r0b0t5_kn0w_h0w_t0_d4nc3}