SANS HolidayHack 2016 Full Writeup

05 Jan 2017
on pwn

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

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.

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:

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.

tweets=api.user_timeline(screen_name='santawclaus',count=350)all_tweets=list(tweets)# Save last_idlast_id=all_tweets[-1].id# Use `max_id` to get tweets after last_idtweets=api.user_timeline(screen_name='santawclaus',count=350,max_id=last_id)all_tweets.extend(tweets)# Print everythingfortweetinall_tweets:print(tweet.text)

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?

BUGBOUNTY

2) What is inside the ZIP file distributed by Santa’s team?

SantaGram APK

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:

~/workspace/jadx/build/jadx/bin/jadx SantaGram_v4.2.apk

Using 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

Using -C5 we want to display 5 lines of context around our match of password. In our results, we have the following:

4) What is the name of the audible component (audio file) in the SantaGram APK file?

discombobulatedaudio1.mp3

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:

Elf House #2 - To open the door, find both parts of the passphrase inside the /out.pcap file

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 itchy using sudo.

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

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 less or more. If that is the case, we can use the command feature of less or 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.

* -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.

discombobulatedaudio2.mp2

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 Alter X, 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.

frompwnimport*r=remote('dungeon.northpolewonderland.com',11111)r.sendline('GDT')# Enter Debug menur.recvuntil('GDT>')# Recv until we read the next promptforxinxrange(20):r.sendline('TK')# Call Take actionr.sendline(str(x))r.recvuntil('GDT>')r.sendline("EX")# Exit Debug menur.sendline("I")# Print all items to see what we have retrievedr.interactive()

And testing the script..

Now we could increase 20 to 200 or 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 peppermint@northpolewonderland.com, 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.

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 false to 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 false to true. We can then rebulid the APK into a patched version.

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.

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 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)

Administrator access

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 (Password1!, Fall2016, 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 AUTH cookie.

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.

Ah, there is a query field along with the name and 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 view.php.

Hmm.. we can only download files if we are guest. One idea could be to UPDATE the 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:

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.

$ sox discombobulatedaudio*wav full.wav

This 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.

Father Christmas, Santa Claus. Or, as I've always known him, Jeff!

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 IanLanceTaylorJr

This calculated byte is passed to putchar for printing

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 gdb.

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.

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 rspeak_:

A number calculated elsewhere is passed to rspeak_:

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 rspeak_

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.

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!

Cory Duplantis

I am a senior security researcher for Cisco Talos and play on Samurai for CTFs. Being happily married, CTFs, tool development, and singing barbershop take up the majority of my time. This blog is the home for my CTF writeups, development tricks, and other random hacker tips.