Another year has past, which means the SANS HolidayHack is in full swing. This year, many new technologies were used, which were a blast to dig into. This writeup dives into each challenge and the methodology used to solve it. A summary of the story this year is below
- Part 1: A Most Curious Business Card
- Part 2: Awesome Package Konveyance
- Part 3: A Fresh-Baked Holiday Pi
- 5) What is the password for the “cranpi” account on the Cranberry Pi system?
- 6.1) How did you open each terminal door…
- Elf House #2 - To open the door, find both parts of the passphrase inside the /out.pcap file
- Workshop #1 - To open the door, find the passphrase file deep in the directories.
- Santa’s Office - GREETINGS PROFESSOR FALKEN.
- Workshop #2 - Find the passphrase from the wumpus. Play fair or cheat; it’s up to you.
- Workshop – Train Station - Train Management Console
- 6.2) …and where had the villain imprisoned Santa?
- Part 4: My Gosh… It’s Full of Holes
- 7.1) The Mobile Analytics Server (via credentialed login access)
- 7.2) The Dungeon Game
- 7.3) The Debug Server
- 7.4) The Banner Ad Server
- 7.5) The Uncaught Exception Handler Server
- 7.6) The Mobile Analytics Server (post authentication)
- 8) What are the names of the audio files you discovered from each system above?
- Part 5: Discombobulated Audio
- BONUS FEATURES - Dungeon Game 2.0
Sitting restless in their bed on Christmas Eve, Josh and Jess Dosis hear Santa work his magic downstairs in their living room. After Santa gave his "Ho Ho Ho", Josh and Jess heard a loud "Oooomph!" followed by a scuffle of sorts and then... nothing. Josh and Jess hurry to the living room to only see Santa's big blue sack. Jess realizes that Snata has been abducted. The only thing left was Santa's business card.
This business card is the start of the journey of the HolidayHack.
Part 1: A Most Curious Business Card
We see Santa Claus as two different social media accounts:
- Twitter: @santawclaus
- Instagram: @santawclaus
Visiting each of these accounts could prove profitable.
Part 1.1: Instagram
One of the images from Instagram is below:
Besides the mess on the table, two pieces of information stand out in the photo:
A URL: www.northpolewonderland.com
A File: SantaGram_v4.2.zip
Using the popular tool
wget, we can download this file locally:
wget http://northpolewonderland.com/SantaGram_v4.2.zip. Attempting to
unzip this file shows that the file is password protected.
Let’s see if the Twitter handle has anything useful we could use as a password for this zip file.
Part 1.2: Twitter
We begin by looking at Santa’s tweets at his handle of @santawclaus:
These look a bit like gibberish. Let’s try to print all of the tweets and see if we see anything useful. The
tweepy Python package makes this process very simple.
In order to use
tweepy, we need to retrieve API access tokens from our Twitter account. This page gives a great tutorial on how to create a new application. This new application gives specific access tokens used by
tweepy to authenticate to Twitter.
Now that we have our access tokens, let’s prepare our environment to use
tweepy. In order to isolate our workspace to only
tweepy, we can use
virtualenv. This way, we won’t clutter our system Python packages. We can also use virtualenvwrapper to provide an easy interface for working with these enviroments.
tweepy now is a breeze:
The prefix of
(tweepy) confirms that we are now in a isolated environment for
tweepy. If we need to leave this
virtualenv in the future, we can
deactivate it to return to using the system Python packages.
We can use
tweepy to retrieve tweets of one user using the following API:
The tweets could have a hint as to what the password could be for the SantaGram zip file.
So BUGBO isn’t the right password. Just to be sure, let’s check how many tweets we retrieved via
200?! Hmm.. the API must limit to 200 tweets pre request. Looking at the API for user_timeline shows that we can retrieve other tweets using the
max_id keyword argument. Let’s ask for the other 150 after our last tweet.
Let’s see what the other tweets have in store:
Ah, BUGBOUNTY was the password. And now we have the SantaGram APK! This gives us the answers to the first two questions..
1) What is the secret message in Santa’s tweets?
2) What is inside the ZIP file distributed by Santa’s team?
Now that we have the APK, let’s move on to Part 2!
Part 2: Awesome Package Konveyance
Our task now is to find a username/password combination in the APK. It is possible to retrieve decompiled source of the APK using jadx. After installing
jadx, executing it over our APK is simple:
grep would be just fine, but Silver Searcher is absolutely amazing and fast. After installing
the_silver_searcher, we can search the entire code base:
ag -C5 password SantaGram_4.2
-C5 we want to display 5 lines of context around our match of
password. In our results, we have the following:
And here we have the answer of our next question:
3) What username and password are embedded in the APK file?
guest / busyreindeer78
Now we need to find an embedded audio file in the APK. In order to retrieve the data in the APK, we can use apktool. Extracting data using
apktool can be done as follows:
apktool d SantaGram_v4.2.apk
Using a simple
find command in the resulting directory for various audio file extensions will surely find our hidden audio file:
And we have the answer to question 4!
4) What is the name of the audible component (audio file) in the SantaGram APK file?
Almost half way there! Onward we go!
Part 3: A Fresh-Baked Holiday Pi
In order to progress in the Quest, we have to find all of the pieces to our Cranberry Pi. After finding all of the pieces in game, we now have access to the Cranberry image as well as various terminals, also in game. Our next task is to retrieve the password for the cranpi account on the image.
Let’s take a look at what is in the Cranberry Pi image. A very handy tool when looking at firmware images is binwalk.
binwalk attempts to extract various file types by looking for byte sequences specific to particular file formats. Extracting the image using
binwalk is a breeze:
binwalk -e cranbian-jessie.img
There are a few false positives, but we can look for any extracted file systems:
We have one file system. Let’s mount it and examine the innerds.
Bazinga! Now that we have the filesystem mounted, let’s see if we have an
etc/shadow file which could contain the encrypted
cranpi account password
With our encrypted password in hand, let’s use john the ripper to crack this password using the rockyou.txt wordlist.
After patiently waiting for a few minutes, we have our cracked password and the answer to question 5.
5) What is the password for the “cranpi” account on the Cranberry Pi system?
Now that we have our password to the Cranberry Pi, let’s go through each of the terminals in game.
6.1) How did you open each terminal door…
The next series of questions were from terminals within the game itself.
Elf House #2 - To open the door, find both parts of the passphrase inside the /out.pcap file
scratchy@c8089dc3a829:/$ whoami scratchy scratchy@c8089dc3a829:/$ ls -la /out.pcap -r-------- 1 itchy itchy 1087928 Dec 2 15:05 /out.pcap
Looks like we are the
scratchy user but
/out.pcap is owned by
itchy. One method of running other commands as another user is via the
sudo command. Let’s look at what we can possibly run as
scratchy@dfb2060be020:/$ sudo -l sudo: unable to resolve host dfb2060be020 Matching Defaults entries for scratchy on dfb2060be020: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin User scratchy may run the following commands on dfb2060be020: (itchy) NOPASSWD: /usr/sbin/tcpdump (itchy) NOPASSWD: /usr/bin/strings
Now we can simply run
itchy against the
scratchy@61a080518c85:/$ sudo -u itchy strings out.pcap <html> <head></head> <body> <form> <input type="hidden" name="part1" value="santasli" /> </form> </body> </html>
Boom! Part 1 is
santasli. The second part is a bit trickier. We can try to change the encoding schemes used by strings to extract part2. Here we are wanting to extract strings of 16-bit little endian.
scratchy@fd947cbbfb0c:/$ sudo -u itchy strings -e l out.pcap sudo: unable to resolve host fd947cbbfb0c part2:ttlehelper
Combining the two parts gives us the key to the door:
Workshop #1 - To open the door, find the passphrase file deep in the directories.
We have to find a passphrase. Again, utilizing
find is a great way to discover such files.
elf@ef46088d503b:~$ find . . ./.bashrc ./.doormat ./.doormat/. ./.doormat/. / ./.doormat/. / /\ ./.doormat/. / /\/\\ ./.doormat/. / /\/\\/Don't Look Here! ./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you? ./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/' ./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/'/key_for_the_door.txt ./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/cookbook ./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/temp ./.doormat/. / /\/\\/Don't Look Here!/secret ./.doormat/. / /\/\\/Don't Look Here!/files ./.doormat/. / /\/\\/holiday ./.doormat/. / /\/\\/temp ./.doormat/. / /\/santa ./.doormat/. / /\/ls ./.doormat/. / /opt ./.doormat/. / /var ./.doormat/. /bin ./.doormat/. /not_here ./.doormat/share ./.doormat/temp ./var ./temp ./.profile ./.bash_logout
key_for_the_door.txt is the file we want to grab. So the challenge here is to traverse the weird directory structure given. Below is one possible solution to retrieving the key file.
elf@9e5b15c1546f:~$ cd .doormat elf@9e5b15c1546f:~/.doormat$ cd ". " elf@9e5b15c1546f:~/.doormat/. $ cd \ elf@9e5b15c1546f:~/.doormat/. / $ cd \\ elf@9e5b15c1546f:~/.doormat/. / /\$ cd \\\\ elf@9e5b15c1546f:~/.doormat/. / /\/\\$ cd D* elf@9e5b15c1546f:~/.doormat/. / /\/\\/Don't Look Here!$ cd Y* elf@9e5b15c1546f:~/.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you? $ cd \' elf@9e5b15c1546f:~/.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you? /'$ cat key* key: open_sesame
Santa’s Office - GREETINGS PROFESSOR FALKEN.
We see the phrase “GREETINGS PROFESSOR FALKEN”. This is a reference to the movie Wargames.
Side note: If you are reading this or playing the HolidayHack and haven’t seen this movie, you simply have to stop reading and watch the movie before proceeding. That is all.
We have to replicate the Wargames interaction in order to be given the passphrase. We can follow this script to help through the terminal prompts.
GREETINGS PROFESSOR FALKEN. Hello. HOW ARE YOU FEELING TODAY? I'm fine. How are you? EXCELLENT,g IT'S BEEN A LONG TIME. CAN YOU EXPLAIN THE REMOVAL OF YOUR USER ACCOUNT ON 6/23/73? People sometimes make mistakes. YES THEY DO. SHALL WE PLAY A GAME? Love to. How about Global Thermonuclear War? WOULDN'T YOU PREFER A GOOD GAME OF CHESS? Later. Let's play Global Thermonuclear War. ,------~~v,_ _ _--^\ |' \ ,__/ || _/ /,_ _ / \,/ / ,, _,,/^ v v-___ | / |'~^ \ \ | _/ _ _/^ \ / / ,~~^/ | ^~~_ _ _ / | __,, _v__\ \/ '~~, , ~ \ \ ^~ / ~ // \/ \/ \~, ,/ ~~ UNITED STATES SOVIET UNION WHICH SIDE DO YOU WANT? 1. UNITED STATES 2. SOVIET UNION PLEASE CHOOSE ONE: 2 AWAITING FIRST STRIKE COMMAND ----------------------------- PLEASE LIST PRIMARY TARGETS BY CITY AND/OR COUNTRY NAME: Las Vegas LAUNCH INITIATED, HERE'S THE KEY FOR YOUR TROUBLE: LOOK AT THE PRETTY LIGHTS Press Enter To Continue
And there we have the passphrase for this terminal:
LOOK AT THE PRETTY LIGHTS
Workshop #2 - Find the passphrase from the wumpus. Play fair or cheat; it’s up to you.
We are presented a game where we are in a room and can traverse to other rooms via a
move command. During our adventure, we have to kill the evil Wumpus via a
shoot command. While moving, we are given a warning via
*sniff* (I can smell the evil Wumpus nearby!). When this message occurs, we can shoot the adjacent rooms and we should hit the Wumpus.
You are in room 7 of the cave, and have 5 arrows left. *whoosh* (I feel a draft from some pits). *sniff* (I can smell the evil Wumpus nearby!) There are tunnels to rooms 3, 16, and 18. Move or shoot? (m-s) s 3 You are in room 7 of the cave, and have 4 arrows left. *whoosh* (I feel a draft from some pits). *sniff* (I can smell the evil Wumpus nearby!) There are tunnels to rooms 3, 16, and 18. Move or shoot? (m-s) s 16 *thwock!* *groan* *crash* A horrible roar fills the cave, and you realize, with a smile, that you have slain the evil Wumpus and won the game! You don't want to tarry for long, however, because not only is the Wumpus famous, but the stench of dead Wumpus is also quite well known, a stench plenty enough to slay the mightiest adventurer at a single whiff!! Passphrase: WUMPUS IS MISUNDERSTOOD
This terminal gives us access to the DFER.
Workshop – Train Station - Train Management Console
We are presented with the Train Management Console.
==== MAIN MENU ==== STATUS: Train Status BRAKEON: Set Brakes BRAKEOFF: Release Brakes START: Start Train HELP: Open the help document QUIT: Exit console
In order to
START the train, the brakes must be off AND we have to have the management password.
menu:main> START Checking brakes.... Enter Password:
Looking at the
HELP, we immediately see the filename of a file being shown. This probably means this screen is being displayed via
more. If that is the case, we can use the command feature of
more to execute a command via
!. Let’s give it a try.
Now that we can execute commands, let’s see what is in all the files in the directory.
We now have the password for the train (
24fb3e89ce2aa0ea422c3d511d40dd84) and can start it.
6.2) …and where had the villain imprisoned Santa?
After going through the time traveling train, we are sent to the year 1976. Looks like Santa was imprisioned in the DFER in 1976.
Part 4: My Gosh… It’s Full of Holes
Hurray, we found Santa. Now we get to participate in Santa’s bug bounty program. Here we target the various servers that are related to SantaGram in order to try to retrieve the various audio files. Strap in.. here we go!
We can find the hostnames of the servers by a simple search in our
apktool output directory.
We want to make sure we know what ports are open on which hosts. Let’s write a quick for loop to run
nmap over all ports on every host.
Quick nmap options:
* -p- - Scan for all ports * -oA $t - Save all output formats with prefix of the host * $t - The host to run nmap on
7.1) The Mobile Analytics Server (via credentialed login access)
After logging into the application via the credentials found in the APK (
guest / busyreindeer78), there is an MP3 button that immediately downloads an audio file:
We are presented with our next needed audio file.
7.2) The Dungeon Game
Ports relevant for the Dungeon Game:
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 11111/tcp open vce
Port 80 shows some basic rules and commands to use while playing the text-based dungeon crawler. Our goal is to reach the lair of a michievous Elf who can trade us secrets that could help our quest.
We are also given the dungeon game binary through one of the characters in game. While we’re sure the dungeon game itself is fun and the story is quite intriguing, since we have the binary, hacking that seems more interesting ;-) Let’s take a look at how we can cheat at the game to achieve the goal.
After verifying the server is indeed on port 11111 via
nc dungeon.northpolewonderland.com 11111, we need a client to communicate with the dungeon server. This sounds like a perfect place to use pwntools.
pwntools will give us an easy API for communicating with the server.
Now that we have a client, let’s see what we can discover about the binary iteself. Something that is immediately intruging is a particular path at the beginning of
main that performs a
strcmp against the string
GDT (at address
0x419a34). Let’s look at this path in Binary Ninja.
Let’s see if this
GDT command does anything special in the game.
Ok, we have found a menu that has various actions as
Display X, and
Take. Let’s see if the
Take command actually does anything for us.
Now we have the ability to give ourselves whatever item we want. Let’s add this functionality to our client to get the first 20 items.
And testing the script..
Now we could increase
300 in order to fill our inventory with all possible items in the game. There was another interesting field in the debug menu called
Alter Here. Let’s experiment a bit with this functionality.
Now that we know we can move between rooms based on number, let’s mimic our items script with a room script to see the description for each of the rooms available in the dungeon.
This script will start spitting out descriptions of all rooms. After running the script, we notice that room 192 has some very interesting text about an Elf:
>$ l You have mysteriously reached the North Pole. In the distance you detect the busy sounds of Santa's elves in full production. You are in a warm room, lit by both the fireplace but also the glow of centuries old trophies. On the wall is a sign: Songs of the seasons are in many parts To solve a puzzle is in our hearts Ask not what what the answer be, Without a trinket to satisfy me. The elf is facing you keeping his back warmed by the fire.
This sounds like we have to give the Elf some kind of fancy item in order to satisfy it. In order to achieve this, we can modify our first items script to jump to room 192 afterwards. We can then examine our inventory and try to give the elf something fancy.
It looks like that worked! After emailing
firstname.lastname@example.org, we receive our much deserved audio file.
You tracked me down, of that I have no doubt. I won't get upset, to avoid the inevitable bout. You have what you came for, attached to this note. Now go and catch your villian, and we will alike do dote.
7.3) The Debug Server
Ports relevant for the Debug Server (
PORT STATE SERVICE 80/tcp open http
Let’s see where this server is used in our APK.
Looks like the only location of this host is found in
EditProfile. Let’s get a bit more context to how this string is used.
Looks like when
run() is called on a
C08591 object, we execute something related to this host, potentially sending traffic to it. Let’s look for where this object is used elsewhere in the code.
Ok, so this is interesting.. We only create a
C08590 object if
z is true and
z is true only when string
2131165214 equals true. Let’s look for this new valuable string in our source.
Hmm.. there doesn’t appear to be another use of this id number. Maybe the number is referenced via its hex value?
There we go. This string is referenced by “debug_data_enabled”. And where is this string located?
So ultimately, a value from
strings.xml is referenced in order to create traffic to
dev.northpolewonderland.com. In order to test this theory, we need to patch the APK and set this string from
true and see if any new traffic is generated from the app.
One great choice for testing Android applications is Genymotion. Genymotion leverages Virtualbox to provide Android virtual machines. Once we have our application installed on the VM, we can use Charles in order to catch the various API calls. In order for the VM to proxy through Charles, we need to setup the proxy. Note: Your IP will be your host Virtualbox IP.
Now that we have our Genymotion VM proxying through Charles.. We need to patch the APK to enable the debug feature.
We can take our decoded APK (but not decompiled) and modify the debug string from
true. We can then rebulid the APK into a patched version.
In order for the APK to be installed, we need to sign our patched copy. In order to do this we need our own keystore. Once we have our keystore, we can then sign our APK.
Now that we have our patched, signed APK, we can use
adb (Android Debugger) to install the APK into our VM.
Now that we have our app installed, let’s test our path to see if
dev.northpolewonderland.com shows up in Charles.
One useful feature of Charlesproxy is to be able to grab the request in cURL form and use that as a test bed.
Taking a look at the request, we notice that it is simply JSON.
Looking at the request, there appears to be a filename given back.
Is this a new file that appears on the server?
So yes, that file is available on the server. There is also a new field returned in the response:
"verbose": false. Could we send this field in our initial request, but setting verbose to true?
Holy cow! There are a ton more filenames given in this response. Also, wait.. what is that first file?
Looks like we have our audio file from dev! Simply
wget this file and we can move on to the next server!
7.4) The Banner Ad Server
Ports relevant for the Banner Ad Server (ads.northpolewonderland.com):
PORT STATE SERVICE 80/tcp open http
To begin this journey, let’s begin figuring out what we can access in Meteor. The Developer Console in the browser is our best friend in this case.
After exploring the
Meteor object in the console, we find that
Meteor.routes is an array of the hooked routes. Let’s print all of the URLs on each route to see what we are dealing with.
Our goal now is to traverse each URL and look at each collection via the following idiom.
This dirty work leads to the following combination:
Looks like this particular Object has an “audio” attribute. By simply using
wget, we can download this audio file and put this server behind us. Onward..
7.5) The Uncaught Exception Handler Server
Ports relevant for the Exception Server (ex.northpolewonderland.com):
PORT STATE SERVICE 80/tcp open http
Let’s begin again by looking through the APK source code for our target server.
We know from the debug server that strings are referenced by decimal rather than hex. Let’s search for the decimal value rather than the hex.
Let’s see the context of the use of the string in
Some function called
m4775a uses this host string. Now to find out where this function is used.
PostDetails action looks interesting. What context is being used here?
Ah, so this is an
Unexpected Exception handler. Assuming an unexpected exception occurs, traffic is sent to the exception server. Now we just need to find where this handler is used to discover if we can force an exception.
Now we see the UncaughtExceptionHandler is used in
PostDetails. Let’s look for a place where an Exception might not be handled.
Here we see two try/catch blocks. The first block actually catches all exceptions via
catch (Exception e). This block probably won’t be valuble for us.
The second block though, only catches
ParseException. If we can cause something other than a
ParseException to occur, maybe we can trigger our wanted function.
Let’s dive a bit deeper into
So we are getting a Parse Object via some
Configs option. Let’s look at where this
Configs option is set.
Oh shoot, this is the source code from
jadx. What does this Config look like in the decoded smali code from
So smali is an assembler for the DEX format used by Android’s VM. We want to edit this smali code to try and force an exception. Let’s make an hypothesis to test that if we provide a
POSTS_USER_POINTER that the application wasn’t expecting, then an exception will occur. Let’s change the
Configs.smali with a string that the application probably isn’t expecting.
We can follow the same procedure as the debug server to rebuild and resign the patched APK before uploading it to our local Genymotion VM. After uploading the APK and looking at Posts on the application, we see the following in Charles.
Awesome! We got an exception to occur and recorded the traffic sent to the server. It looks like there is crashdump file created and sent back in the response. Let’s grab that to see if anything special is in it.
Ok, so it won’t be that easy ;-) Let’s try to fiddle with the request to see if we can prompt a different response from the server. Charles makes this easy by allowing us to copy the request in cURL format. Let’s begin with the operation field.
Ah nice! So we now know there are two possible operations:
ReadCrashDump. Let’s see what the
ReadCrashDump operation does for us.
Alright, so we need a
crashdump attribute set. Let’s use our crashdump file returned in the response of our original exception.
Oh, weird.. so the server auto appends
.php for us.. This could remind us of a blog post a little birdie told us about. We could possibly use a PHP Filter to read our the crashdump file instead.
Ah cool! So we can return the source of our crashdump. Sadly, there wasn’t anything new from this file. There was one other file though on the exception server:
exception.php itself. Could we replace our crashdump file with the
exception.php to see what it has in it?
And it works! And lookie there.. we have the path to our next audio file:
discombobulated-audio-6-XyzE3N9YqKNH.mp3. As always, a simple
wget can retrieve the audio file. We are ready to tackle the final server!
7.6) The Mobile Analytics Server (post authentication)
Ports relevant for the Analytics Server (analytics.northpolewonderland.com):
PORT STATE SERVICE 80/tcp open http
A typical good idea when tackling a new web application is to record as much traffic as we can and then analyze it offline. We can use Burp Suite to proxy our traffic for us. After setting our proxy settings in our browser to 127.0.0.1:8080. We can begin exploring the application.
The main functionality is to query a database for various report entries. There is also a functionality to save a query in a report which we can look at later via the report uuid.
After recording traffic from as much of the application that we have access to via our
guest account, we can begin replaying various requests in Burp. One of the initial cases would be the login request. Let’s try a different username. What we are looking for is different messages from the server in order to implicitly gain information about users.
Just from these repsonses, we know that if a valid user, but incorrect password is given, a response of
Bad password! is returned. This means implicitly that the usernme given was correct. Hence, we know that
administrator is a valid user in the database. A few password guesses (
yummycookies) proved to not work, but we still have this new piece of information.
While looking at the
query.php POST request, we notice we can receive an interesting SQL error message from the server.
Looks like whatever type we query for, this is inserted into a
SELECT statement looking for the table
app_OURTHING_report. While this is intriguing at first, it seems to be hard to leverage into any sort of information leakage.
One common occurance for web application is for developers to accidently push their code repository into production. Is there any sort of code repository available?
Fantastic! So we have access to the
.git repo. We can now clone the repo and reset the directory to extract the objects from the repository itself.
Great, now we can search through the source code of the application. Since we know there is an
administrator user, let’s see how user’s are authenticated. Looking back over the request, we notice there is an
Let’s look for how this
AUTH cookie is generated.
AUTH cookie is a json blob with
date keys. This blob is then encrypted via some encryption function. Is there also a decrypt function somewhere?
Just to make confirm our hypothesis, let’s try to decrypt our current
Now that we know our
AUTH cookie is set for the
guest user, let’s try to make a new cookie for the
administrator user that we know exists from our earlier testing.
In order to test our new cookie, let’s look to see if there is any new functionality that is only accessed via the
Ok, so we need to send a request
/edit.php with our new
AUTH cookie. If this request sends a valid response, we know our new cookie succeeds.
And now we have access to this edit feature as
Retrieving the treasure
Now that we have our
administrator access, it is time to figure out how we can leverage this
edit.php page to find our last audio file.
edit.php page can modify the name and description of any given report. Let’s look exactly how this works in the script.
After giving a valid report ID (which we can generate by querying and saving the report), the script takes any fields after the
id and updates the corresponding table in the database. The key feature here is the following.
This allows us to provide any valid entry to update. We need to know what all the fields are in the
reports table. Luckily, we have the SQL schema in the repository as well.
Ah, there is a
query field along with the
description fields we already knew about. Looking further in the source, we see that this
query field is executed whenever we view a previous report via
With all of this information, we think we now have the steps to execute arbitrary SQL queries:
- Create a query report and save the ID
- Log in as
editthe report’s query field
- View the report and see if our custom query is executed
Our report currently has ID
80677516-9fc8-451b-8da3-e8148691db75. Because we are curious for what the
administrator password actually is, let’s test with the query
SELECT * from users.
Bazinga! We can now execute arbitrary SQL commands. We also know the
administrator password is
We only need to find the audio file now. Let’s look for a relevant SQL table that should contain the mp3.
Alrighty, simple. The query
SELECT * from audio should return the relevant filename and ID.
Looking at the relevant
getaudio.php should allow us to download with the newly found ID to retrieve our final audio file.
Hmm.. we can only download files if we are guest. One idea could be to
audio table such that the
discombobulatedaudio7.mp3 has a username of
guest. Sadly, we can’t execute that command, so we need to find something a bit more tricky.
We can’t show the raw bytes of the mp3 file? Could we do something like base64 encode the mp3 file so that we can retrieve it from the webpage? Let’s try to execute a query like
SELECT TO_BASE64(mp3) from audio where filename like '%7%'
And there it is! All that is left now to do is extract the base64 blob and decode it. With that, we have all 7 audio files! Now to figure out who is behind kidnapping Santa!
8) What are the names of the audio files you discovered from each system above?
Just to make sure we have all the files, here are all of the audio files found:
APK Asset - discombobulatedaudio1.mp3 Analytics Guest - discombobulatedaudio2.mp3 Dungeon Game - discombobulatedaudio3.mp3 Dev - debug-20161224235959-0.mp3 Ads - discombobulatedaudio5.mp3 Exception Server - discombobulated-audio-6-XyzE3N9YqKNH.mp3 Analytics Administrator - discombobulatedaudio7.mp3
Part 5: Discombobulated Audio
(Huge thanks for those who have read this far.)
Our final challenge is to piece together the audio file and figure out what all of the pieces combined say.
One easy way we can combine all of the audio files is with sox. Sox gives us the ability to combine files, but they need to be WAV files first. To convert all of our MP3 files to WAV, we can use mpg321. This process is quite simple.
Now we have all of our WAV files in
./wavs, let’s use
sox to combine all of the files into one large file.
full.wav sounds a bit slowed down. For this, we can employ Audacity
The phrase spoken in the audio is the last remaining passphrase for the final door.
For the answers to questions 9 and 10, what better explaination than from the villan himself.
9) Who is the villain behind the nefarious plot.
10) Why had the villain abducted Santa?
Well there you have it. Another great year of holidayHack. We really like binary knowledge here on ctfhacker.com, so why not dig a little deeper into the dungeon binary to see what else we can find. Shall we?
BONUS FEATURES - Dungeon Game 2.0
BONUS 1 - Reversing the encryption of The Dungeon Game
Because we enjoy binaries and reversing, let’s dig into what it would take to statically reverse the configuration file read by the dungeon binary. Let’s look at the final text after we finished the game earlier.
The elf says - you have conquered this challenge - the game will now end. Your score is 10 [total of 585 points], in 3 moves. This gives you the rank of Beginner.
Let’s look for each of these strings in the binary.
Recap of the strings and if they are found in the binary:
the elf says - no your score - yes gives you the rank - no
Interesting, the “gives you a rank” string is printed immediately after the “Your score” string, but isn’t found in the binary. There must be something else near that prints it.
In the same function that prints the “Your score” string, there is a call to
rspeak_. This function in turns calls
rspsb2nl_. This is the leg work function. After reversing this function, we know a bit more about what is going on.
- A given number is given to the function
- This number is used to reference for another number in memory (num2)
- Num2 is converted to an index in the configuration file
- Bytes are read from this index and xored with the key of
- This calculated byte is passed to
- Once a null byte is calculated, the function returns
We want to dump all possible strings in the configuration, which means we need to automate this process. The most difficult part the steps is retrieving
num2 from memory. We can ease this problem by dumping all of the reference numbers from memory using
After reversing, we know that the reference numbers are stored at
0x625c08. A quick gdb command can save the bytes at this address to a file.
dump dbmemory.dat 0x625c08 0x625c08+0x1000
Now that we have the reference numbers, a quick Python function can be used to extract the given reference number for our wanted string.
We now have all the pieces to dump all the strings in the configuration. The script below will dump all possible strings from the configuration.
Let’s see the results for the first 25 strings.
Hm.. now that we have the strings.. wouldn’t it be nice to be able to see these strings in our disassembler? Incoming BONUS 2!
BONUS 2 - Adding the decrypted strings to Binary Ninja
Looking through the various calls to
rspeak_, there are two possible situations:
A number is passed to
A number calculated elsewhere is passed to
For simplicity’s sake, we will only handle the top case for this example.
Our task will involve the below steps:
- Find all occurances to
- Look at the previous instruction
- If that instruction is similar to
mov edi, NUMBER, extract the number
- Call our
rspeak()function we created above
- Write the resulting string as a comment next to the function call
Binary Ninja’s Python API makes this process quite simple. Let’s look at each of the API calls to achieve each step. The
bv object is the current BinaryView object given to us from Binary Ninja that gives us access to the API from our current analysis.
Find all occurances to
Look at the previous instruction
If that instruction is similar to
mov edi, NUMBER, extract the number
rspeak() function we created above
Write the resulting string as a comment next to the function call
The full script, along with our
dumpdb.py script, can be dropped in the
~/.binaryninja/plugins. This script can then be imported and called in Binary Ninja. If all works out, we should see comments of strings next to our
rspeak_ function calls.
Thanks again to Counterhack for putting on another fantastic Holiday Hack! Thank you for sticking with me throughout this writeup. I hope you learned a little something from this. Have a great New Year! Happy hacking!For relevant code for this writeup:
git clone https://github.com/ctfhacker/ctf-writeups