CSAW Quals 2015 - ArpaHack

2015-09-22 16:58:26

ArpaHack was two web challenges in one site, K_{Stairs} and K_{Achieve}, originally scored at 50 and 150 points. Eventually they were increased to 100 and 200 points. The first challenge was to win the game, and the second was to win without taking damage.
The game itself is bunch of tiles of different types which you walk on. Grass tiles do nothing, lava tiles damage you, mystery tiles have a chance to kill you, hole tiles kill you, and wall tiles block you and damage you if you try to move onto them. There is also a food counter, which decreases every step you take. Once you run out of food you begin taking damage every step instead.
To be able to go any lengthy distance, the player needs a lot of food. The only way to get food is in the DLC store. However there is no way to actually get the tokens for the dlc.
We found that the tokens are stored only in the session, and not with the user, and that each time a user is registered, they are given 3 tokens to start. So by creating a bunch of new accounts, we could make as many tokens as we wanted, and play the game for the first flag.

However at the end it is impossible to not take damage, as the goal stairs are surrounded by lava.

So we had to find a way to modify the map or cheat our damage count.
The game stores the map and player data in local storage, both base64 encoded, and separated by a colon.
The player data appeared to change completely every second, so we assumed that it was encrypted.
The map data was also strange, but stayed consistent. Looking closely at the bytes it appeared that there were several rows of 0xff-0x0 with a few changed values every now and then. This patter was also offset by 0x48. Assuming that nulls would be the most likely byte in the map we eventually ran it through an xor with the 0xff-0x0 starting at 0x48. This gave us a large file with the map encoded with each tile being a nibble. Here it is an example of a map color coded:

We planned to modify this and then re-xor the bytes. However when we tried this we got "State Corrupted". Looking back at the map, we noticed that there was one extra byte at the end, which looked like some sort of checksum. Not knowing how the checksum was calculated, we bruteforced this one byte by making POST requests to /status. Once it returned that the map was valid, we put it into local storage and refreshed the page, putting us right next to the stairs so we could get our second flag.