Hacking Google CTF - Episode 1

Hacking Google CTF - Episode 1

This is a write-up about how I solved the second episode of the H4CK1NG GOOGL3 security CTF. If you didn't read my post about the first episode, I highly recommend you to read it, since it contained one of the most fun challenges of this CTF.

Challenge 01: Wannacry

A screenshot of the hex values of the challenge file

Hint: The Linux file and strings utilities are very useful

In the first challenge, only a binary file is provided. After looking at the hex values of the binary file and printing the strings of the file header using strings challenge.bin | head it became apparent that this is a gzip compressed file. file challenge.bin further revealed that it is .zip compressed. Unzipping this file resulted in a challenge.tar.gz file which I could extract with tar -xzvf challenge.tar.gz. This gave me two files: flag and wannacry.

The file command revealed that the wannacry is an actual executable and the flag is an OpenPGP secret key. The file name of the executable led to some hesitation at first but after running it through several malware scanners, I determined that it is safe to execute.

> ./wannacry
Usage of ./wannacry:
  -encrypted_file string
        File name to decrypt.
  -key_file string
        File name of the private key.

Interesting, so the wannacry executable can use some private key to decrypt our secret key flag file. But where do we get the private key from?

From the first episode, we know what a flag looks like. So why not search for it in the strings of the binary using strings wannacry | grep h4ck1ng. This returned not the flag, but the URL https://wannacry-keys-dot-gweb-h4ck1ng-g00gl3.uc.r.appspot.com/ which is a list of 200 private key files.

My friend quickly decided to brute force the key and wrote a script to try every private key on the secret key flag. Eventually, we found one that worked and got the unencrypted flag file that contained the actual flag!

Challenge 02: Wannacry the second

A screenshot of the hex values of the second challenge file

** Hints:**

  • The strings might be intresting again

  • Execute the hidden function

Challenge 01 was a nice warm-up, but challenge 02 is a whole other level. It comes with a binary file, which unzips to a binary executable called wannacry, again. However, this time, executing the binary prints nothing and just returns the error code 0. So it looked like I would have to figure out some secret parameters in order to get the executable to print something. Looking at the strings again reveals another URL: wannacry-killswitch-dot-gweb-h4ck1ng-g00gl3... A website that just displays the text "Our princess is in another castle." - a reference (and a meme) to from the game Super Mario.

The executable seems to contain a whole dictionary at the end, containing various strings. The string princess is definitely in there together with lots of other alphabetically sorted words.

Looking at the output of the file command more specifically, shows us that it is "not stripped" and therefore contains debug symbols:

> file wannacry
wannacry: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0c23340ab6c6d0c158f0ee356a1deb0253d8cf4c, for GNU/Linux 3.2.0, not stripped

Looking at some of the debug symbols in the binary strings, I spotted that the time library is used. This led to my first suspicion that this might be some time-based encryption. At this point, I saw no other way than reverse engineer the executable with static analysis - which I have never done before.

So I downloaded Ghidra, which is a software suite developed by the NSA to reverse engineer software. The static analysis showed that the executable always immediately exists. But there is more code - there is just no way to execute it (legitimately). Looking at that code revealed that they actually generate time-based passwords (TOTP) using SHA-1 and print them.

Now there are two options:

  1. Reverse engineer the TOPT algorithm and implement it myself

  2. Somehow get the print() function to execute.

The second option seemed way more interesting to me. Since the binary contained symbols, we should be able to attach a debugger to the process. So I read into how gdb is used and run the following commands:

break main # we want to set a breakpoint in the main method before the software exists
run # run the program until we hit our breakpoint
j print # continue to execute the program in the print function

That revealed a URL together with one of the words from the dictionary:

The GDB output

Visiting this URL right after running the program generated it, shows this page:

Visiting the valid URL

Clicking the button that is displayed, reveals the flag. If you wait too long, the page will just show the message about the princess again.

Challenge 03: Chess 2.0

The chessboard of the second challenge

Hints:

  • Look at the PHP source code

  • Magic methods are interesting

Another chess challenge - exciting! This time, however, there is no admin panel. Also looking at the diff between the old and new HTML file, we can see that this time the flag is not printed upon winning the game:

echo "<h1>Winning against me won't help anymore. You need to get the flag from my envs." . "</h1>";

Also, the load_board.php file was changed: It now only allows loading files with the extension .php. So we cannot read the environment variable from /proc/self/environ.

Luckily the exploit from the chess challenge from episode 1 to get the web app's source code still works. Looking at the diff again, the code is the same as in challenge 01. While solving challenge 01, I already suspected that you might be able to modify how the stockfish (the chess engine, see my post about the first episode for more details) binary is called. Would there be a way to exploit this?

After going through the PHP source code a dozen times, there is one piece of code I couldn't make sense of. The __wakeup() method of the class Stockfish that contains the whole code which talks to the chess engine process:

    public function __wakeup()
    {
        $this->process = proc_open($this->binary, $this->descriptorspec, $this->pipes, $this->cwd, null, $this->other_options) ;
        echo '<!--'.'wakeupcalled'.fgets($this->pipes[1], 4096).'-->';
    }

Reading the PHP docs about magic methods reveals that __sleep() and __wakeup() are used to run code before serialization and after deserialization. The sleep method can be used to clean up and the wakeup method to reestablish database connections. Or in our case, it opens the stockfish binary to re-establish a connection with the chess engine. The weird thing is, that it also echos the stdout of the process. Maybe that was used for debugging?

Ok fine, we now have understood all the backend code and the wakeup thing is legit - and now? Well, now I was stuck. After some more spending some more hours looking at the code, I spotted another suspicious piece of code. Rember where I tried to teleport the chess pieces using the move_end method in episode 1?

Turns out there is actually a vulnerability there:

$movei = unserialize(base64_decode($_GET['move_end']));

This code, used for evaluating where a chess piece was dragged to, deserializes WHATEVER we pass to it. Looking at the docs for unserialize(), they explicitly state:

If the variable being unserialized is an object, after successfully reconstructing the object PHP will automatically attempt to call the __unserialize() or __wakeup() methods (if one exists).

Oh damn. This means we can pass a PHP class to it that the current environment recognizes, set its variables and have the __wakeup() method execute. And in this case, wakeup() actually executes a process and returns the stdout to us.

So, I copied the code and serialized an instance of the stockfish class which contains the env command for the variable that normally would hold the path to the stockfish binary. This is then base64 encoded and passed to the move_end parameter, like so:

https://hackerchess2-web.h4ck.ctfcompetition.com/?move_end=Tzo5OiJTdG9ja2Zpc2giOj...

And voila, we now see ... one environment variable? Turns out only the first line of the command output is written to the HTML. So the final step was to transform the output of the env command to one line: env | xargs.

Now we can see a list of all environment variables as comments in the HTML. And there is the flag:

<!--wakeupcalledGATEWAY_INTERFACE=CGI/1.1 CONTENT_TYPE=multipart/form-data; boundary=--------------------------355823505429505671295364 SHLVL=1 REMOTE_ADDR=10.119.223.201 QUERY_STRING=move_end=Tzo5OiJTdG9 [...]
SERVER_ADDR=10.120.3.109 REDIRECT_FLAG2=https://h4ck1ng.google/solve/rc[...]
-->

Conclusion

Another fun challenge! This one was definitely harder than the previous one - whose write-up you can read over here. Or continue with episode 3 and read my write-up for the next episode over here.

Episode Overview: