Points: 498 (solved by 2 teams)
Category: Web, RE


In this challenge we were given a URL of a web service – http://hhvm.ctf.site:10080/ and two shell commands which were used to run the service:


About hhvm

HHVM is a virtual machine for PHP developed by Facebook, which uses JIT to accelerate code execution. PHP scripts are converted into HipHop bytecode (HHBC), optimized and then compiled into native machine code.

By default, HHVM works similarly to Zend Engine – it loads and runs PHP files on demand. Although flexible, this method is grossly inefficient since it gives little room for advanced optimizations. The alternative is to use “repo authoritative” mode (used in this challenge) in which HHVM builds a SQLite3 database ( hhvm.hhbc) with highly-optimized bytecode and additional metadata required to run all scripts.


Obtaining the hhvm.hhbc

The first thing we need to do is to extract the repo file. Luckily for us it is easily accessible, since Proxygen (built-in HTTP server) serves all files from the directory it was run. We just run

You can download it here.

Discovering PHP files

Now, when we have the repo file, we can see what is happening under the hood. First, let’s see what endpoints we can find on the server. To do this we can simply load the database and look around for hints. One of the interesting tables contains:

Dumping the bytecode

Next step is to find out what both of those scripts are doing. In order to do this, we can pass a -vEval.DumpBytecode=1 flag to HHVM.

This command will extract and print HipHop bytecode from repo. I’ve included dumps of both files for reference, but I’ll skip to manually “reversed” PHP code. I encourage you to try and analyze HHBC by yourself. It works as a simple stack machine. Look here for HHBC specification.




Ok, now that we have rewritten bytecode to PHP, we can look for a way to pwn the server.


This script can run any shell command but requires an unknown token. Let’s leave it for now.


This script is much more interesting. To generate a valid token, we have to send a correct password, however as we can see in lines 3 and 5, password is truncated to 4 letters and then converted to integer. Those transformations reduce search space to numbers up to 4-digits long – 10 000 different passwords. Additionally, after logging in, a token is generated and we are redirected to /shell.php?token=[token]&cmd=cat%20/etc/slow-webshell.txt, so this probably displays the file containing flag.

Logging in

Ok, now we know everything to get to the shell. The first thing to do is to break the password. We’ll do this by simply iterating over all 10 000 numbers, computing 16xMD5 hash of candidate and using password_verify for final verification.
After finding a valid password we have two options: either use it in the login form and let the server compute a valid token or generate it locally and directly access shell.php.

Here’s a script which finds both password and a token (warning – takes a lot of time to finish):

After finding the token, we can simply curl the URL from shell-login.php to reveal the flag: