SANS Holiday Hack Challenge 2024 Writeup

Walkthrough

Posted on January 13, 2025

Introduction

TLDR

In 2024, the Holiday Hack Challenge is about the elves fighting each other.

It was a lot of fun again and I hope I can help people with this walkthrough in case they get stuck somewhere.

First login and intro

figure

Sign in with Email/Password, Google or X:

figure

Click “Play Now!”:

figure

Prologue

Goto Jingle Ringford to get the Snowball Badge:

figure figure figure figure figure figurefigure figure

After we have the snowball badge you can start your first terminal challenge:

First Terminal

figure figure figure figure figure figure figure

So from the conversation you now should search for Poinsettia McKittens and Angel Candysalt. Both can be found nearby.

figure

Elf Connect

Talk to Angel Candysalt:

figure figure

figure

So let’s play connections :)

figure figure figure figure figure figure figure figure

Angel Candysalt gives some more information to manipulate the score itself in the code. If you look into the code in the browser you see:

figure

figurefigure

The score is set based on sessionStorage score. After the first round I tried to change it from 400 to 400000. But editing it directly in session storage does not work:

figure

So the next idea was to set a breakpoint at a line where the score is updated. There is a line where a new line connections is awarded with 100 points. So let’s set a breakpoint here:

figure

The next time you have a correct line of connections the breakpoint will be triggered and you can set up an arbitrary value:

figure

If you now disable all breakpoints and let the program continue you will get the gold level of this challenge:

figure

Now let’s search for Poinsettia McMittens:

figure figure figure

Elf Minder 9000

figure

The following where the solutions I found in the usual way to play Elf Minder 9000:

figure figure figure figure figure figure figure

figure

figure figure

figure figure

After that I tried to find the gold level:

figure

figurefigure figure

If you look into the code you see that there is an edit mode:

figure

Data seems to be stored as json:

figure

There is also a strange variable named whyCantIHoldAllTheseThings:

figure

And Websockets seems to be used:

figure

There is a function disappointHackers:

figure

Looking above the comments mentioning edit there is an interesting variable isEditor:

figure Double Excalamation marks mean convert to boolean true if not empty

https://hhc24-elfminder.holidayhackchallenge.com/index.html?id=171bcaee-cb15-43de-852d-c987458aacc9&level=A%20Real%20Pickle&edit=true goes in the edit mode

figure figure

This could lead to something like:

figure

But you cannot end this level.

The game is based on json:

figure figure figure

But I haven’t found out how I can use this to solve the gold challenge.

Act I

In your snowball click to go to Act I:

figure figure

cURLing

On of the first places you find is:

figure

Bow Ninecandle introduces you to the task:

figure figure figure

Possible solutions are:

figure figure

y

figure figure

curl curlingfun:8080 -> 
"""
You have successfully accessed the site on port 8080!

If you need help, please remember to run "hint" for a hint!
"""

figure

curl -k https://curlingfun:9090

figure

curl -X POST -d "skip=alabaster" -k https://curlingfun:9090

figure

curl --cookie "end=3" -k https://curlingfun:9090

figure

curl -I -k https://curlingfun:9090

figure

curl -H "Stone:Granite" -k https://curlingfun:9090

figure

curl -k --path-as-is  'https://curlingfun:9090/../../etc/hacks'

figure

Look into the folder:

ls 

figure

There is a file explaining hard mode:

cat HARD-MODE.txt

To solve these constraints run:

figure figure

curl -H "Hack:12ft" --cookie "end=10" -X POST -d "skip=bow" -k https://curlingfun:9090

figure

Ok then access this URL:

curl -H "Hack:12ft" --cookie "end=10" -X POST -d "skip=bow" -k --path-as-is 'https://curlingfun:9090/../../etc/button'

figure

Finally follow the redirection with -L:

curl -L -H "Hack:12ft" --cookie "end=10" -X POST -d "skip=bow" -k --path-as-is 'https://curlingfun:9090/GoodSportsmanship'

figure

"""
Excellent work, you have solved hard mode!  You may close this terminal once HHC grants your achievement.
"""

After the challenge is solved perhaps Bow Ninecandle tells you a little bit additional:

figure figure figure

The next I visited was Alabaster Snowball left to the curling area:

figure

figure figure

At the left of this area is Tinsel Upatree:

figure figure So this probably gives the hint to look into the crates.

A little bit lower at the left side is Noel Boetie:

figure figure

There is a monitoring station in the middle:

figure figure figure figure

Frosty Keypad

Right at the top you can find Morcel Nougat and a keypad challenge:

figure figure figure figure figure figure

The book can be found here:

figure figure

And there is a UV flashlight:

figure

The book contains the following text:

figure figure figure figure

figure figure

figure

figure

figure

To proceed you can read about the Ottendorf Cipher: https://nationaltreasure.fandom.com/wiki/Ottendorf_Cipher

There is a hint at the postit at left upper side:

figure figure

So from the Ottendorf cipher this is the combination “Page:Word:Character in Word” which results in

SANTA

With the UV flash light you can identify the numbers used. This can narrow the set to find the combination of numbers.

figure figure figure figure figure figure

The numbers are 2,6,7 and 8.

What could the word SANTA be in numbers? I found the solution at the mobile phone - T9:

figure

figure

Now let’s try to permutate 2,6, 7 and 8, brute force the key pad and find the gold solution. I took the HTTP request from the web site put it into python using the requests library. You have to toggle the speed a bit to get correct solution in the responses. I took 1 second:

import requests
import time
from itertools import product

def submit_answer(answer):
    # Define the URL, headers, and data
    # url = "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=c5e47b3c-3816-4a83-81e2-d76d6be16286"
    url = "https://hhc24-frostykeypad.holidayhackchallenge.com/submit?id=b973b6b2-ab9f-43a4-8554-7bad47c15195"
    headers = {
        "User-Agent": "<your User Agent>",
        "Accept": "*/*",
        "Accept-Language": "de,en-US;q=0.7,en;q=0.3",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Referer": "https://hhc24-frostykeypad.holidayhackchallenge.com/?&challenge=termFrostyKeypad&username=IeFeec2e&id=b973b6b2-ab9f-43a4-8554-7bad47c15195&area=frontyardact1&location=62,22&tokens=easy,termFrostyKeypad,hard&dna=ATATATTAATATATATATATTACGATATATATTATAGCATATATATATATATTAGCATATATATATATGCCGATATTATAATATATATATATGCGCATATATATATATATCGATATTACG",
        #"Referer": "https://hhc24-frostykeypad.holidayhackchallenge.com/?&challenge=termFrostyKeypad&username=IeFeec2e&id=c5e47b3c-3816-4a83-81e2-d76d6be16286&area=frontyardact1&location=62,23&tokens=&dna=ATATATTAATATATATATATTACGATATATATTATAGCATATATATATATATTAGCATATATATATATGCCGATATTATAATATATATATATGCGCATATATATATATATCGATATTACG",
        "Content-Type": "application/json",
        "Origin": "https://hhc24-frostykeypad.holidayhackchallenge.com",
        "Alt-Used": "hhc24-frostykeypad.holidayhackchallenge.com",
        "Connection": "keep-alive",
        "Cookie": "CreativeCookieName=eyJ1c2VyaWQiOiI1NzgxYjBkMi02YTdjLTRjNTEtYWZlZS0zMmVkZmUxODVmNmEifQ.Z1I3Qg.iRNJPtIXeYJt9XFndzErrrNeiOE",
        #"Cookie": "CreativeCookieName=eyJ1c2VyaWQiOiIwODlmY2MwNS04ZmI2LTRmNDctODc0Ni01OThiYzVhOWEwNDAifQ.Zz594g._3M5KLOrZW-wLC6ilcHuGEoUM5k",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "DNT": "1",
        "Sec-GPC": "1",
        "Priority": "u=0",
        "TE": "trailers",
    }
    data = {"answer": answer}  # Replace with your actual answer

    # Send the POST request
    response = requests.post(url, headers=headers, json=data)
    time.sleep(1)

    # Print the response status code
    print(answer+ " "+ str(response.status_code))

    # If successful (status code 200), you can print the response content
    # if response.status_code == 200:
    #    print(response.text)

if __name__ == "__main__":
    # Loop through potential answers (replace 100000 with the desired upper limit)
    for combo in product('2678', repeat=5):
    #for combo in product('8039', repeat=5):
    #for combo in product('9140', repeat=5):
        submit_answer(''.join(combo))

This yields in a lot of 400 HTTP Status Codes:

400
{"error":"The data you've provided seems to have gone on a whimsical adventure, losing all sense of order and coherence!"}

and finally to:

figure If you grep in the output: figure you have the gold solution! :) figure So the first answer is from the word SANTA at the Keypad:

72682

and by using brute force:

22786

Shredded Pieces

If you talk to Morcel Nougat again

figure

you get shredded pieces

figure figure The whole text from Morcel: figure

and you have a new item in your pocket:

figure

Morcel Nougat has a hint for you: figure

as well as Jewel Loggins: figure

So let’s use the mentioned github gist https://gist.github.com/arnydo/5dc85343eca9b8eb98a0f157b9d4d719:

mkdir shredded_pieces
cd shredded_pieces
mv ~/Downloads/shreds.zip .
curl -LO https://gist.githubusercontent.com/arnydo/5dc85343eca9b8eb98a0f157b9d4d719/raw/78aa80e7fcead23fdd7f890b466a5f00ad956b7d/heuristic_edge_detection.py 
unzip shreds.zip
sudo apt install python3-numpy

After that you can use python3 heuristic_edge_detection.py and as a result get assembled_image.png:

figure

From the picture you can extract:

BAUD: 112500
PARITY: EVEN
DATA: 7 BITS
STOP BITS: 1 BIT
FLOW CONTROL: RTS

Hardware Hacking

At the right side there is a hardware hacking challenge with Jewel Loggins:

figure figure figure

There is also Wombley Cube a little bit more above:

figure figure

Hardware Part 1

Let’s tackle the hardware challenge:

figure

figure

With the information from shredded pieces you can piece together the shreds and the configuration (cf shredded pieces)

BAUD: 115200
PARITY: EVEN
DATA: 7 BITS
STOP BITS: 1 BIT
FLOW CONTROL: RTS

I used the AI which responded to the colors with:

**Frequently used color assignments:**

- Red:** Often used for the positive supply voltage (VCC).
- Black:** Usually used for ground (GND).
- Green:** Often used for the TX (Transmit) pin.
- Blue:** Often used for the RX (Receive) pin.
- White:** Can be used for various signals, but is less common than the other colors.

and therefore set TX to RX and RX to TX with the corresponding colors.

With the aforementioned settings it burned the chip:

figure

So change 5V to 3V. After that it works:

figure

figure

Now Jewel Loggins tells you a little bit more

figure figure figure

Hardware Part 2

Let us now turn to the left-hand side “Hardware Part 2”

figure

figure

If you check the folder and look into the bash history you can find the following

figure

slh --passcode CandyCaneCrunch77 --set-access 1 --id 42

Lets use that to grant access to card number 42

figure

Jewel Loggins now gives you a hint for gold

figure figure figure

So let’s relogin to the shell. If you look into the folder you can find a file access_cards which is an sqlite database

figure figure

It has two tables access_cards and config. What is inside the access_cards table

select * from access_cards 

figure

Card ID 42 has the following data

sqlite> select * from access_cards where id=42;
42|c06018b6-5e80-4395-ab71-ae5124560189|0|ecb9de15a057305e5887502d46d434c9394f5ed7ef1a51d2930ad786b02f6ffd

Let’s take a short glimpse into the config table

sqlite> select * from config;
1|hmac_secret|9ed1515819dec61fd361d5fdabb57f41ecce1a5fe1fe263b98c0d6943b9b232e
2|hmac_message_format|{access}{uuid}
3|admin_password|3a40ae3f3fd57b2a4513cca783609589dbe51ce5e69739a33141c5717c20c9c1
4|app_version|1.0

The signature from card id 42 is most probably SHA2-256

figure

The format for the hmac message format from the config table says

{access}{uuid}

From this format and the hint from Jewel Loggins to CyberChef, let’s try to create the HMAC.

Open CyberChef and search in the left upper corner in the search box for “HMAC” and move it in the recipe panel. Then write in the input panel (according to {access}{uuid} - uuid is the second row in the access_cards table). Set key format to UTF-8 and the key to the HMAC secret found from the config table.

1c06018b6-5e80-4395-ab71-ae5124560189

figure

The result is the missing signature which can be used to update the card number 42:

135a32d5026c5628b1753e6c67015c0f04e26051ef7391c2552de2816b1b7096
UPDATE access_cards
SET 
id=42,
uuid='c06018b6-5e80-4395-ab71-ae5124560189', 
access=1,
sig='135a32d5026c5628b1753e6c67015c0f04e26051ef7391c2552de2816b1b7096'
WHERE id = 42;

figure Jewel Loggins tells nothing new after that.

Alabaster Snowball: figure figure Wombley Cube: figure

Frosty’s Beach

There is some stuff going on at Frosty’s Beach the context of which I have not yet understood:

figure figure figure

figure figure figure

figure

figure figure

Act II

If you enter Act II the following will happen

figure

Alabaster Snowball gives you a bit information about what happened

figure figure

Toilet Tunnel Network

Sparkle Redberry tells you about some hidden tunnel which you can probably use

figure figure

You can those hidden tunnel in small toilets which you then can use as tunnel between Alabaster area, Wombley and a DMZ

figure figure

Mobile Analysis

The first you can talk to Eve Snowshoes who opens a new task

figure figure figure figure figure

Eve gives you two hints

figure figure

This is the debug version

figure

and this the release

figure

If you open SantaSwipe.apk with Genymotion you can see

figure

Install jadx and start the gui

figure

SantaSwipe.apk with JADX-GUI reveals a database in the code

figure

    private static final String DATABASE_NAME = "naughtynicelist.db";  
    private static final int DATABASE_VERSION = 3;  
  
    /* JADX WARN: 'super' call moved to the top of the method (can break code semantics) */  
    public DatabaseHelper(Context context) {  
        super(context, DATABASE_NAME, (SQLiteDatabase.CursorFactory) null, 3);  
        Intrinsics.checkNotNullParameter(context, "context");  
    }  
  
    @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("DELETE FROM NiceList;");  
        db.execSQL("DELETE FROM NaughtyList;");  
        db.execSQL("DELETE FROM NormalList;");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Carlos, Madrid, Spain');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Aiko, Tokyo, Japan');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Maria, Rio de Janeiro, Brazil');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Liam, Dublin, Ireland');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Emma, New York, USA');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Chen, Beijing, China');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Fatima, Casablanca, Morocco');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Hans, Berlin, Germany');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Olga, Moscow, Russia');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ravi, Mumbai, India');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Amelia, Sydney, Australia');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Juan, Buenos Aires, Argentina');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Sofia, Rome, Italy');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ahmed, Cairo, Egypt');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Yuna, Seoul, South Korea');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ellie, Alabama, USA');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Lucas, Paris, France');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Mia, Toronto, Canada');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Sara, Stockholm, Sweden');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ali, Tehran, Iran');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Nina, Lima, Peru');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Anna, Vienna, Austria');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Leo, Helsinki, Finland');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Elena, Athens, Greece');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Davi, Sao Paulo, Brazil');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Marta, Warsaw, Poland');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Noah, Zurich, Switzerland');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ibrahim, Ankara, Turkey');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Emily, Wellington, New Zealand');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Omar, Oslo, Norway');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Fatou, Dakar, Senegal');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Olivia, Vancouver, Canada');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ethan, Cape Town, South Africa');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Santiago, Bogota, Colombia');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Isabella, Barcelona, Spain');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ming, Shanghai, China');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Chloe, Singapore, Singapore');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Mohammed, Dubai, UAE');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Ava, Melbourne, Australia');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Luca, Milan, Italy');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Sakura, Kyoto, Japan');");  
        db.execSQL("INSERT INTO NormalList (Item) VALUES ('Edward, New Jersey, USA');");  
    }

If you search for NaughtyList in the files

figure

Let’s look into the DELETE FROM NaughtyList WHERE Item = Statement in more detail. This seems to be vulnerable to SQL Injection because the code uses string concatenation

figure

There is also a SELECT statement which filters a specific name in the same file

figure

This is the answer for who has been left out from the list.

Solution:

Ellie

figure

Talk to Eve Snowshoes again

figure

figure

He gives you two hints for the gold solution

figurefigure

First I started with some investigations using JADX-GUI in SantaSwipeSecure.aab.

There you can find the naughtynicelist.db

figure

and some interesting inital data

figure

You can use Genymotion to get some visual experience how SantaSwipeSecure looks like

  959  keytool -genkey -alias server -validity 365 -keyalg RSA -keystore keystore 
  962  java -jar bundletool-all-1.17.2.jar build-apks --bundle=<path_to_SantaSwipeSecure_aab>/SantaSwipeSecure.aab --output=<path_to_SantaSwipeSecure_aab>/output.apks --mode=universal --ks=<path_to_SantaSwipeSecure_aab>/keystore --ks-key-alias=server --ks-pass=pass:keystore
  964  mv output.apks generate_apk/output.apks
  965  cd generate_apk
  967  cp output.apks output.zip
  968  unzip output.zip -d <path_to_SantaSwipeSecure_aab>/generate_apk/

figure

Back in SantaSwipe.apk in the file DatabaseHelper you can find this function

figure

and

figure

If you convert this to java code

cat Charsets.java
import java.nio.charset.Charset;

public final class Charsets {
public static final Charsets INSTANCE = new Charsets();
public static final Charset ISO_8859_1;
public static final Charset US_ASCII;
public static final Charset UTF_16;
public static final Charset UTF_16BE;
public static final Charset UTF_16LE;
public static final Charset UTF_8;
private static volatile Charset utf_32;
private static volatile Charset utf_32be;
private static volatile Charset utf_32le;

    private Charsets() {
    }

    static {
        Charset forName = Charset.forName("UTF-8");
        UTF_8 = forName;
        Charset forName2 = Charset.forName("UTF-16");
        UTF_16 = forName2;
        Charset forName3 = Charset.forName("UTF-16BE");
        UTF_16BE = forName3;
        Charset forName4 = Charset.forName("UTF-16LE");
        UTF_16LE = forName4;
        Charset forName5 = Charset.forName("US-ASCII");
        US_ASCII = forName5;
        Charset forName6 = Charset.forName("ISO-8859-1");
        ISO_8859_1 = forName6;
    }

    public final Charset UTF32() {
        Charset charset = utf_32;
        if (charset != null) {
            return charset;
        }
        Charset forName = Charset.forName("UTF-32");
        utf_32 = forName;
        return forName;
    }

    public final Charset UTF32_LE() {
        Charset charset = utf_32le;
        if (charset != null) {
            return charset;
        }
        Charset forName = Charset.forName("UTF-32LE");
        utf_32le = forName;
        return forName;
    }

    public final Charset UTF32_BE() {
        Charset charset = utf_32be;
        if (charset != null) {
            return charset;
        }
        Charset forName = Charset.forName("UTF-32BE");
        utf_32be = forName;
        return forName;
    }
}
cat Decrypt.java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class Decrypt {

    private final byte[] iv;	
    private final SecretKeySpec secretKeySpec;
    private final byte[] encryptionKey;
  
    public Decrypt() {
        String obj = "rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=";
        byte[] decode = Base64.getDecoder().decode(obj);
        this.encryptionKey = decode; 
        this.secretKeySpec = new SecretKeySpec(decode, "AES");
        obj = "Q2hlY2tNYXRlcml4";
        decode = Base64.getDecoder().decode(obj);
        this.iv = decode;
    }
     
    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.getDecoder().decode(encryptedData));
            if (doFinal == null) return "";
            return new String(doFinal, Charsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        Decrypt d = new Decrypt();
        System.out.println(d.decryptData("Base64todecrypt"));
    }
}

Take the following as the input for the decryptData function

figure

and run this code

java Decrypt

the output is

figure

Another interesting value to decrypt is

figure

which yields to

figure

Oh! This is something deleting from the list. Let’s inspect that:

The output KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8= decrypted is

figure

So the solution then for gold would be

Joshua

figure

Snowball Showdown

The next thing I tackled was the Snowball Showdown. This is where Dusty Giftwrap is located

figure figure figure

So the task is

figure

Run Snowball Showdown in Chrome or Safari - Firefox is really laggy

figure figure

In the background web sockets are used

figure figure

If you start the game in the url you can find a key value pair which controls if the game is single player

figure figure

Set it to true to play alone

figure

From the files copy phaser-snowball-game.js to a local file

figure

Set a breakpoint and disbalance team strength

figure

figure

data.alabasterElves.push(data.alabasterElves[0])

I tried to add elves to increase team of Alabaster

figure

On the other side I tried to decrease the size of the team from Wombley

data.wombleyElves = data.wombleyElves.pop()

Then I played and try to get more points, finally resulting in bronze rating for the challenge

figure figure

Drone Path

For the next challenge go through the tunnels in the DMZ to Chimney Scissorsticks

figure figure figure

The task is the following

figure

So let’s start!

figure figurefigure figure

I downloaded the file and viewed it with a KML viewer in the internet

figure

So this looks like DROP1 ? But this you cannot use anywhere. From the characters left this seems to be incomlete. So I analyzed the file fritjolf-Path.kml deeper using cat.

Mm, there seems to be a link to google earth. So open the KML with Google Earth

figure

If you import the KML file here you get this

figure

And this is the needed password for login. The username you can get from the filename. The credentials:

fritjolf/GUMDROP1

figure figure figure

There is the next challenge

figure

Download CSV. Let’s inspect what’s inside.

For that I used libreoffice: libreoffice Preparations-drone-name.csv and imported the csv there.

figure

The longitude and latitude coordinates there yields to a route https://maps.app.goo.gl/uGa2YFD12wx4VDLF6

figure

If you inspect those coordinate with google maps you can find some interesting thing. If you from map to satellite and look at the environment you can see:

figure

This is definitely an E :)

figure

Here is an L!

figure

F

figure

- or I

figure

H

figure

A

figure

W

figure

K

So we have ELF-HAWK or ELFIHAWK

If you search for this as a Drone you get

figure

So you have a new CSV file.

I would say the keywords LONG and LATTER are a hint for Longitude and Latitude inside the file

figure

For longitude values you can use

cut -f 5 ../ELF-HAWK-dump.csv -d "," | paste -s -d,

For the latitude values

cut -f 6 ../ELF-HAWK-dump.csv -d "," | paste -s -d,

figure

Copy those values into a geopandas python script. geopandas needs a CRS. The most commonly used in the US seems to be EPSG: 4326:

WGS84 (EPSG: 4326) - Commonly used by organizations that provide GIS data for the entire globe or many countries. CRS used by Google Earth

Overview of Coordinate Reference Systems (CRS) in R

So you can set EPSG:4326 / WGS84 (https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84) as CRS

cat dronepath.py 
import pandas as pd
from shapely.geometry import LineString
import geopandas as gpd
import folium

# prepare data
data = {'Latitude': [<Latitude values comma separated],
         'Longitude': [<Longitude Values comma separated>]}
df = pd.DataFrame(data)

# create lines
geometry = [LineString([(lon, lat) for lat, lon in zip(df['Latitude'], df['Longitude'])])]
gdf = gpd.GeoDataFrame(geometry=geometry)

# create map
m = folium.Map(location=[df['Latitude'].mean(), df['Longitude'].mean()], zoom_start=5)
gdf.crs = "epsg:4326"
folium.GeoJson(gdf).add_to(m)
m.save('flugroute.html')

figure

So the final solution (for silver) is

DroneDataAnalystExpertMedal

figure

Back to Chimney Scissorsticks

figure figure

On the Wombley side there is Garland Candlesticks

figure figure

and Wombley Cube

figure figure

PowerShell

Piney Sappington introduces to a new challenge about PowerShell

figure figure

The solutions are

figure

y

figure

dir
type welcome.txt

figure figure figure

type welcome.txt | Measure-Object -word

figure

netstat -an

figure

Invoke-WebRequest http://127.0.0.1:1225

figure

figure

function BasicAuth($user, $pass, $url, $cookie_name, $cookie_value) {
 $pair = "$($user):$($pass)"
 $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
  $headers = @{
  Authorization = "Basic $encodedCreds"
 }
 
 if (-not $cookie_name)
 {
	$response = Invoke-WebRequest $url -Headers $headers
 }
 else 
 {
	 $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
	 $cookie = New-Object System.Net.Cookie
	 $cookie.Name = $cookie_name
	 $cookie.Value = $cookie_value
	 $cookie.Domain = "127.0.0.1"
	 $session.Cookies.Add($cookie);
	 $response = Invoke-WebRequest $url -Headers $headers -WebSession $session
 }
 
 $response
}

function BasicAuth($user, $pass, $url, $cookieHashTable) {
 $pair = "$($user):$($pass)"
 $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
  $headers = @{
  Authorization = "Basic $encodedCreds"
 }
 
 if (-not $cookieHashTable)
 {
	$response = Invoke-WebRequest $url -Headers $headers
 }
 else 
 {
	 $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
	 foreach ($element in $cookieHashTable.getEnumerator()) {
		 $cookie = New-Object System.Net.Cookie
		 $cookie.Name = $element.Key
		 $cookie.Value = $element.Value
		 $cookie.Domain = "127.0.0.1"
		 $session.Cookies.Add($cookie);
	 }
	
	 $response = Invoke-WebRequest $url -Headers $headers -WebSession $session
 }
 
 $response
}
# Use it like BasicAuth admin admin http://127.0.0.1:1225 @{token ="xyz"; mfa_token ="abc"}
$response = BasicAuth "admin" "admin" "http://127.0.0.1:1225"

figure

$response.Links

figure

$links = $response.Links

foreach ($link in $links) {                                             
$href = $link.href
$r = Invoke-WebRequest $href
$x = ($r.Content -split '\s+').Count
if ($x -eq 138) { Write-Host $href }
}

figure

So there is one interesting endpoint http://localhost:1225/endpoints/13. Let’s look into it

$r = Invoke-WebRequest "http://localhost:1225/endpoints/13"
$r.Content

figure figure

There is a hint for a csv file what you can analyze

$r=Invoke-WebRequest http://127.0.0.1:1225/token_overview.csv
$r.Content

figure

This seems to need more so let’s try to authenticate us with the Basic Auth credentials already used

$r =  BasicAuth "admin" "admin" "http://127.0.0.1:1225/token_overview.csv"
$r.Content

figure figure

Mm, from the response you can infer http://127.0.0.1:1225/tokens/<sha256sum>. Call this page and evaluate the response

$r=BasicAuth "admin" "admin" "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C"
$r.Content 

figure figure

A cookie is missing. Our BasicAuth function can handle this with the 3rd and 4th parameter

$r=BasicAuth "admin" "admin" "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" @{token="5f8dd236f862f4507835b0e418907ffc"} 
$r.Content      

figure

The response

<h1>Cookie 'mfa_code', use it at <a href='1734742884.0846226'>/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C</a></h1>

is an MFA code which you can use at the mentioned endpoint url.

figure figure

So those two approaches did not work, let’s try to put the code as cookie this way:

figure figure figure

It’s seems that the site needs two cookies, the token cookie and the mfa cookie.

Our written Basic function works like this:

# Use it like BasicAuth admin admin http://127.0.0.1:1225 @{token ="xyz"; mfa_token ="abc"}

so you can do

$r=BasicAuth admin admin http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C @{token = "5f8dd236f862f4507835b0e418907ffc"; mfa_code = "1734822972.8962357"}
$r.Content

And from the response you can infer instead of mfa_code (which was given you as hint before) , it seems that a cookie named mfa_token is expected. Let’s try that:

$r=BasicAuth admin admin http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C @{token = "5f8dd236f862f4507835b0e418907ffc"; mfa_token = "1734822972.8962357"}
$r.Content

figure

Oh, this time took it too long, so probably you need to code something to make it faster. Let’s check that and do the last steps again:

figure

Seems only the part for href is changing if you compare it to the last time which is the value for mfa_token. To extract you can do

$html = $r.Content
$match = $html | Select-String -Pattern "<a href='(.*?)'>"
$href = $match.Matches.Groups[1].Value

This uses

figure

The complete snippet then is

$r=BasicAuth "admin" "admin" "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" @{token="5f8dd236f862f4507835b0e418907ffc"} 
$html = $r.Content
$match = $html | Select-String -Pattern "<a href='(.*?)'>"
$href = $match.Matches.Groups[1].Value
$r=BasicAuth admin admin http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C @{token = "5f8dd236f862f4507835b0e418907ffc"; mfa_token = $href}
$r.Content

figure

Response:

$r.Content                                                              
<h1>[+] Success</h1><br><p>Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg==</p>

Looks like base64. To decode it you can implement

$encodedString = "Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg==" 
$decodedBytes = [System.Convert]::FromBase64String($encodedString) 
$decodedString = [System.Text.Encoding]::UTF8.GetString($decodedBytes) 
Write-Host "Decoded String: $decodedString"

which gives you the silver solution

figure

The final secret is:

SnowLeopard2ReadyForAction

To get gold, there seems currently to be no clue inside the terminal. So let’s close the terminal and talk to Piney Sappington again who gives you some hints

figure figure figure figure figure

North Pole Monitor Station

There is also a monitor station again in the middle in the DMZ

figure figure figure figure figure

The Great Elf Conflict

Near Pepper Minstix and Wunorse Openslae in the DMZ you can find the Great Elf Conflict challenge.

Pepper Minstix tells you

figure figurefigure

From Wunorse Openslae you get

figure figure

This results in multiple tasks - from what there first are

figure

To start the challenge create for Microsoft KC7 and account or login using Google or Microsoft. You are greated with the following text

figurefigure

There is a small introduction to the web interface what tells you where clusters, query results can be found, or queries be made

figure figure figure

The intro text tells you the solution for the first question

let's do this

Question 2 pops up

figure

Let’s perform the steps described

figure

Employees

which gives you a list of all employees

figure

Employees
| take 10

filters you the first 10 entries of this list

figure

At available tables you can get an overview of all tables existing

figure figure

So to end question 2

figure

enter the mentioned text

when in doubt take 10

Question 3 asks to count the employees

figure figure

The solution:

90

figure

Now specific entries of the table are of interest

figure figure figure

Enter

Shinny Upatree

figure

In the next step operators are explained

figure

To proceed enter the text described

operator

In question 6 two tables are connected the first time. Get the email address from Angel Candysalt from the Employee table and then put it into the query in the Email table.

figure figure

figure figure

So the email address i

angel_candysalt@santaworkshopgeeseislands.org

Now let’s us it like in the text mentioned to retrieve the amount of recipients

figure figure

Solution:

31

The next question is about how to get how many distinct recipients for a specific sender are in the logs. The sender to be analyzed is twinkle_frostington@santaworkshopgeeseisland.org. This can be done with:

figure figure figure

Solution

32

Ok, so Twinkle Frostington has written to 32 different recipients. In the next question the task this gets broadened to websites.

figure figure

So the Employees table contains IPs (look before). Get the IP from Twinkle from it.

figure

And search it as source IP in the outbound network events as Twinkle should be the source for the website calls. From the results lets check how many different urls Twinkle has visited

figure figure

Solution:

4

This was it for level 1, you reached level 2 ! :) The first question here is how many domains visited contain the word “green”

figure figure figure figure

The solution is

10

Now we have to handle two queries where the first is used as a variable in the second

figure figure figure figure

figure

We got the solution:

8

This is also the solution for silver Microsoft KC7 “KQL 101”:

figure figure

To start level 2

figure

just enter

surrender

This level is about attacks against Team Wombley. The next question is more lengthy

figure figure

So now there is no help anymore. You have to get the answer from what we learned already. First let’s check what information you can get from the Email table:

figure

There is a column verdict which labels an email from the spam filter as phishing or not The keyword mentioned in the task to search for in the emails is “surrender”

figure

Solution:

surrender@northpolemail.com

figure

To find the amount of elves from Team Wombley who received the email do

figure

Solution:

22

figure

Mm, so how to find the filename? There is a column called link in the Email table which could fit:

figure

So from all those links you can infer the solution:

Team_Wombley_Surrender.doc

figure

Let’s use this snippet to find the patient zero, the first computer infected in Team Wombley

figure

Solution:

Joyelle Tinseltoe

figure

There are two variables to be found timestamp and hostname. The timestamp can be used from the last query response:

figure

The ProcessEvents table contains a column hostname. Let’s search for the hostname from Joyelle Tinseltoe in this table.

figure figure

The corresponding query gives us the solution

figure

So this gives us

keylogger.exe

The next step is about getting the flag for the second challenge in the main Microsoft KC7 challenge “Operation Surrender”. You have to encode the last solution keylogger.exe

figure figure figure

Solution:

a2V5bG9nZ2VyLmV4ZQ==

Enter this solution for part two in the Microsoft KC7 challenge “Operation Surrender”:

figure figure

The last question for level 2

figure figure

Just enter

snowfall

figure

This finished level 2. Time to start level 3!

figure

In contrast to the last level, level 3 is now about attacks against Team Alabaster. It starts with the task to find the root of a password spraying against this team.

figure

Let’s just use the snippet

figure figure

So the source IP is:

59.171.58.12

In the next question you have to identify into how many accounts were logged into successfully from the attacker IP

figure

If you check the AuthenticationEvents table you can see that there is a column which stores successful logins

figure

Let’s check for the given IP 59.171.58.12 and list the hostnames connected to those successful logins

figure figure

You now only have to count the hostnames together

figure

19

Mm, this didn’t work for me. Ah, it’s not hostname it has to be username! Reason is because at each hostname you can perhaps have multiple users who can log in. Let’s try that

figure

This worked! The solution is:

23

The next task is about identifying a service externally available which was misused.

figure

First I thought perhaps this can be identified using InboundNetworkEvents and the aforementioned attacker source ip.

figure figure

But to no avail. Next I focused on ProcessEvents and search the parent processes I could find

figure

Oh, this is not so much! I wanted to analyze the services, so I thought services.exe or sc.exe is probably correct. I used that to filter for the underlying processes started:

figure

This also lead to a dead end. Finally, I came back to the AuthenticationEvents table from the task before and looked a deeper into it there. If you inspect the column description it carefully you can see that RDP was used.

figure

So this is the answer for the current question.

figure

Ok, your task is to find this exfiltrated file! What is the username for Alabaster`s laptop:

figure

In the description there is a hint that says that you can narrow down the events using the timestamp. I took the time around the results from the last solution

figure

This yields to the solution:

Secret_Files.zip

figure

So now the task is to find the file which encrypted the device. For this you can use the same query from the last question

figure

Solution:

EncryptEverything.exe

In the next step you can catch the next flag and accomplish the third question “Operation Snowfall” for the Microsoft KC7 challenge.

figure figure

Solution:

RW5jcnlwdEV2ZXJ5dGhpbmcuZXhl

figure

The fourth part starts with a simple string to enter

figureSolution:

stay frosty

This part starts to look into Emails from Noel Boetie

figure figure

Solution:

2024-12-12T14:48:55Z

You have to check when Noel clicked the link. For that you first need to identify the link and afterward can analyze the time linked to that.

figure figure

The link in the email is:

https://holidaybargainhunt.io/published/files/files/echo.exe

The corresponding table to connect is OutboundNetworkEvents

figure

Solution:

2024-12-12T15:13:55Z

figure

Ok. To get the IP you can use the table PassiveDns. Passive DNS are historical DNS logs which you can use to analyze incidents.

figure

Solution:

182.56.23.122

figure

This time you use the IP from the attacker server before to check if it was also used to log in into the victim system. This could be if it was just one Kali server for example. Let’s use the AuthenticationEvents table as mentioned.

figure

Solution:

WebApp-ElvesWorkshop

is the hostname in which the IP has logged in.

figure

So now we know the hostname which was probably initially compromised. If you look in the ProcessEvents table for it, you get 8 entries

figure

Solution:

Invoke-Mimikatz.ps1

figure

If you look deeper in the ProcessEvents you can extract that the executed file was echo.exe. And the first time this was executed is

figure

Solution:

2024-12-12T15:14:38Z

Oh, this was it for level 3!

figure

Let’s start level 4 :)

figure

Mm, domain … The table OutboundNetworkEvents has a column url which contains all the urls the users tried to reach from outside.

figure

So the solution is:

compromisedchristmastoys.com

figure

Remember from questions 2-4 it was Noel Boetie who first clicked the phishing link. So lets first inspect which processes ran at this user which had this file frosty.zip used:

figure figure

We can use the timestamp to look what happened in this time range

figure

Solution:

sqlwriter.exe

The solution can be extracted from the same log entry as the last answer

figure

figure

Solution:

frosty

figure

Hey let’s get the badge for the complete module :)

figure

Solution:

ZnJvc3R5

figure

This is also the final solution for the fourth part in the Microsoft KC7 challenge “Echoes in the Frost”:

figure

After you enter the solution you will have gold in the Microsoft KC7 challenge:

figure

Next I moved to Act III

Act III

Entering Act III a lot of new objectives get unlocked

figure

And Santa appears and he seems to be very angry :)

figure

Santa Vision

figure figure

If you go a little bit you come to Ribb Bonbowford:

figure figure figure figure figure figure

The goals are

figure

Ribb also delivers you with some hints

figure figure

So this is a repo about a tool to analyze the file system jffs called jefferson

figure

The technology used in this task seems to be MQTT

If you open the application

figure

There is a small crocodile in the lower right corner to click

figurefigure

The first link goes to

figure figure

In this description the link GateXOR opens:

https://www.youtube.com/watch?v=6JXRHZsV9u0

The video there plays the american pronounciation of “gator”:

figure

figure

The second link “Time Travel” opens a console and starts a server:

figure

If you scan this target you get

➜  santavision sudo nmap -A 34.135.21.17  
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-22 03:21 CET
Nmap scan report for 17.21.135.34.bc.googleusercontent.com (34.135.21.17)
Host is up (0.13s latency).
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE     VERSION
22/tcp   open  ssh         OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey: 
|   256 a9:29:8e:6b:08:91:72:8a:cb:d1:e8:d4:f4:af:5f:c2 (ECDSA)
|_  256 f0:f8:c0:5e:5c:c0:bb:81:99:34:0f:47:35:10:23:f7 (ED25519)
8000/tcp open  http-alt    gunicorn
|_http-title: Santa Vision
|_http-server-header: gunicorn
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 404 NOT FOUND
|     Server: gunicorn
|     Date: Sun, 22 Dec 2024 02:21:53 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 1820
|     Vary: Cookie
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Santa Vision</title>
|     <!-- meta -->
|     <meta name="description" content="">
|     <meta name="author" content="">
|     <meta name="viewport" content="width=device-width,initial-scale=1">
|     <!-- styles -->
|     <!-- CSS only -->
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
|     <link rel="stylesheet" href="/static/css/styles.css">
|     </head>
|     <body>
|     <!-- Navigation -->
|     <header class="p-3 mb-3 text-bg-dark">
|     <div class="container">
|     <div class="d-flex flex-
|   GenericLines: 
|     HTTP/1.1 400 Bad Request
|     Connection: close
|     Content-Type: text/html
|     Content-Length: 193
|     <html>
|     <head>
|     <title>Bad Request</title>
|     </head>
|     <body>
|     <h1><p>Bad Request</p></h1>
|     Invalid Request Line &#x27;Invalid HTTP request line: &#x27;&#x27;&#x27;
|     </body>
|     </html>
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Sun, 22 Dec 2024 02:21:48 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 2946
|     Vary: Cookie
|     Set-Cookie: svCookie=-_GrbgUMaRrs_KzAshOPFe4OztUSHnx-Y38to7B2mbg; Expires=Wed, 22 Jan 2025 02:21:48 GMT; HttpOnly; Path=/
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Santa Vision</title>
|     <!-- meta -->
|     <meta name="description" content="">
|     <meta name="author" content="">
|     <meta name="viewport" content="width=device-width,initial-scale=1">
|     <!-- styles -->
|     <!-- CSS only -->
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
|     <link rel="stylesheet" href="/static/css/styles.css">
|     </head>
|_    <body>
9001/tcp open  tor-orport?
	| fingerprint-strings: 
	|   JavaRMI, Radmin, SSLSessionReq, SSLv23SessionReq, TLSSessionReq, mongodb, tarantool: 
|     HTTP/1.0 403 Forbidden
|     content-type: text/html
|     content-length: 173
|_    <html><head><meta charset=utf-8 http-equiv="Content-Language" content="en"/><link rel="stylesheet" type="text/css" href="/error.css"/></head><body><h1>403</h1></body></html>
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port8000-TCP:V=7.94SVN%I=7%D=12/22%Time=676777BB%P=x86_64-pc-linux-gnu%
SF:r(GenericLines,11E,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x
SF:20close\r\nContent-Type:\x20text/html\r\nContent-Length:\x20193\r\n\r\n
SF:<html>\n\x20\x20<head>\n\x20\x20\x20\x20<title>Bad\x20Request</title>\n
SF:\x20\x20</head>\n\x20\x20<body>\n\x20\x20\x20\x20<h1><p>Bad\x20Request<
SF:/p></h1>\n\x20\x20\x20\x20Invalid\x20Request\x20Line\x20&#x27;Invalid\x
SF:20HTTP\x20request\x20line:\x20&#x27;&#x27;&#x27;\n\x20\x20</body>\n</ht
SF:ml>\n")%r(GetRequest,CA6,"HTTP/1\.0\x20200\x20OK\r\nServer:\x20gunicorn
SF:\r\nDate:\x20Sun,\x2022\x20Dec\x202024\x2002:21:48\x20GMT\r\nConnection
SF::\x20close\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-L
SF:ength:\x202946\r\nVary:\x20Cookie\r\nSet-Cookie:\x20svCookie=-_GrbgUMaR
SF:rs_KzAshOPFe4OztUSHnx-Y38to7B2mbg;\x20Expires=Wed,\x2022\x20Jan\x202025
SF:\x2002:21:48\x20GMT;\x20HttpOnly;\x20Path=/\r\n\r\n<!DOCTYPE\x20html>\n
SF:<html\x20lang=\"en\">\n\x20\x20<head>\n\x20\x20\x20\x20<meta\x20charset
SF:=\"utf-8\">\n\x20\x20\x20\x20<title>Santa\x20Vision</title>\n\x20\x20\x
SF:20\x20<!--\x20meta\x20-->\n\x20\x20\x20\x20<meta\x20name=\"description\
SF:"\x20content=\"\">\n\x20\x20\x20\x20<meta\x20name=\"author\"\x20content
SF:=\"\">\n\x20\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=d
SF:evice-width,initial-scale=1\">\n\x20\x20\x20\x20<!--\x20styles\x20-->\n
SF:\x20\x20\x20\x20<!--\x20CSS\x20only\x20-->\n\x20\x20\x20\x20<link\x20hr
SF:ef=\"https://cdn\.jsdelivr\.net/npm/bootstrap@5\.2\.0/dist/css/bootstra
SF:p\.min\.css\"\x20rel=\"stylesheet\"\x20integrity=\"sha384-gH2yIJqKdNHPE
SF:q0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx\"\x20crossorigin=\"
SF:anonymous\">\n\x20\x20\x20\x20<link\x20rel=\"stylesheet\"\x20href=\"/st
SF:atic/css/styles\.css\">\n\x20\x20\x20\x20\n\x20\x20</head>\n\x20\x20<bo
SF:dy>\n\x20")%r(FourOhFourRequest,7CC,"HTTP/1\.0\x20404\x20NOT\x20FOUND\r
SF:\nServer:\x20gunicorn\r\nDate:\x20Sun,\x2022\x20Dec\x202024\x2002:21:53
SF:\x20GMT\r\nConnection:\x20close\r\nContent-Type:\x20text/html;\x20chars
SF:et=utf-8\r\nContent-Length:\x201820\r\nVary:\x20Cookie\r\n\r\n<!DOCTYPE
SF:\x20html>\n<html\x20lang=\"en\">\n\x20\x20<head>\n\x20\x20\x20\x20<meta
SF:\x20charset=\"utf-8\">\n\x20\x20\x20\x20<title>Santa\x20Vision</title>\
SF:n\x20\x20\x20\x20<!--\x20meta\x20-->\n\x20\x20\x20\x20<meta\x20name=\"d
SF:escription\"\x20content=\"\">\n\x20\x20\x20\x20<meta\x20name=\"author\"
SF:\x20content=\"\">\n\x20\x20\x20\x20<meta\x20name=\"viewport\"\x20conten
SF:t=\"width=device-width,initial-scale=1\">\n\x20\x20\x20\x20<!--\x20styl
SF:es\x20-->\n\x20\x20\x20\x20<!--\x20CSS\x20only\x20-->\n\x20\x20\x20\x20
SF:<link\x20href=\"https://cdn\.jsdelivr\.net/npm/bootstrap@5\.2\.0/dist/c
SF:ss/bootstrap\.min\.css\"\x20rel=\"stylesheet\"\x20integrity=\"sha384-gH
SF:2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx\"\x20cro
SF:ssorigin=\"anonymous\">\n\x20\x20\x20\x20<link\x20rel=\"stylesheet\"\x2
SF:0href=\"/static/css/styles\.css\">\n\x20\x20\x20\x20\n\x20\x20</head>\n
SF:\x20\x20<body>\n\x20\x20\x20\x20<!--\x20Navigation\x20-->\n<header\x20c
SF:lass=\"p-3\x20mb-3\x20text-bg-dark\">\n\x20\x20<div\x20class=\"containe
SF:r\">\n\x20\x20\x20\x20<div\x20class=\"d-flex\x20flex-");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port9001-TCP:V=7.94SVN%I=7%D=12/22%Time=676777BC%P=x86_64-pc-linux-gnu%
SF:r(SSLSessionReq,F5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent-type:\x20t
SF:ext/html\r\ncontent-length:\x20173\r\n\r\n<html><head><meta\x20charset=
SF:utf-8\x20http-equiv=\"Content-Language\"\x20content=\"en\"/><link\x20re
SF:l=\"stylesheet\"\x20type=\"text/css\"\x20href=\"/error\.css\"/></head><
SF:body><h1>403</h1></body></html>")%r(TLSSessionReq,F5,"HTTP/1\.0\x20403\
SF:x20Forbidden\r\ncontent-type:\x20text/html\r\ncontent-length:\x20173\r\
SF:n\r\n<html><head><meta\x20charset=utf-8\x20http-equiv=\"Content-Languag
SF:e\"\x20content=\"en\"/><link\x20rel=\"stylesheet\"\x20type=\"text/css\"
SF:\x20href=\"/error\.css\"/></head><body><h1>403</h1></body></html>")%r(S
SF:SLv23SessionReq,F5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent-type:\x20t
SF:ext/html\r\ncontent-length:\x20173\r\n\r\n<html><head><meta\x20charset=
SF:utf-8\x20http-equiv=\"Content-Language\"\x20content=\"en\"/><link\x20re
SF:l=\"stylesheet\"\x20type=\"text/css\"\x20href=\"/error\.css\"/></head><
SF:body><h1>403</h1></body></html>")%r(JavaRMI,F5,"HTTP/1\.0\x20403\x20For
SF:bidden\r\ncontent-type:\x20text/html\r\ncontent-length:\x20173\r\n\r\n<
SF:html><head><meta\x20charset=utf-8\x20http-equiv=\"Content-Language\"\x2
SF:0content=\"en\"/><link\x20rel=\"stylesheet\"\x20type=\"text/css\"\x20hr
SF:ef=\"/error\.css\"/></head><body><h1>403</h1></body></html>")%r(Radmin,
SF:F5,"HTTP/1\.0\x20403\x20Forbidden\r\ncontent-type:\x20text/html\r\ncont
SF:ent-length:\x20173\r\n\r\n<html><head><meta\x20charset=utf-8\x20http-eq
SF:uiv=\"Content-Language\"\x20content=\"en\"/><link\x20rel=\"stylesheet\"
SF:\x20type=\"text/css\"\x20href=\"/error\.css\"/></head><body><h1>403</h1
SF:></body></html>")%r(mongodb,F5,"HTTP/1\.0\x20403\x20Forbidden\r\nconten
SF:t-type:\x20text/html\r\ncontent-length:\x20173\r\n\r\n<html><head><meta
SF:\x20charset=utf-8\x20http-equiv=\"Content-Language\"\x20content=\"en\"/
SF:><link\x20rel=\"stylesheet\"\x20type=\"text/css\"\x20href=\"/error\.css
SF:\"/></head><body><h1>403</h1></body></html>")%r(tarantool,F5,"HTTP/1\.0
SF:\x20403\x20Forbidden\r\ncontent-type:\x20text/html\r\ncontent-length:\x
SF:20173\r\n\r\n<html><head><meta\x20charset=utf-8\x20http-equiv=\"Content
SF:-Language\"\x20content=\"en\"/><link\x20rel=\"stylesheet\"\x20type=\"te
SF:xt/css\"\x20href=\"/error\.css\"/></head><body><h1>403</h1></body></htm
SF:l>");
Aggressive OS guesses: HP P2000 G3 NAS device (93%), Linux 2.6.32 (92%), Linux 2.6.32 - 3.1 (92%), Infomir MAG-250 set-top box (92%), Ubiquiti AirMax NanoStation WAP (Linux 2.6.32) (92%), Linux 3.7 (92%), Linux 5.0 (92%), Linux 5.0 - 5.4 (92%), Linux 5.1 (92%), Ubiquiti AirOS 5.5.9 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 7 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

...

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 152.56 seconds

Let’s check the http port 8000:

figure

If you open the developer tools you can find the credentials in the comments:

figure

So the solution for Santa Vision A is

elfanon

figure figure

Let’s target Santa Vision B. If you login you see

figure

The url is something like http://34.60.146.232:8000/auth?username=a&pwd=a&server=a&port=a.

If you intercept this with something like Burp Suite you get

figure

If you add id parameter

figure

So there the id parameter missing. In the url you can add something like &id=1 at the end of the url and you get:

figure

If you click at the bottom right you see availables roles

figure

- SiteDefaultPasswordRole
- SiteElfMonitorRole
- SiteAlabsterSAdminRole
- SiteWomblyCAdminRole

And if you click left you see available clients

figure

- elfmonitor
- WomblyC
- AlabasterS

Another port opened is ssh, I tried just to login with the same user like in http, but a private key is needed:

figure

Let’s recap what we have to focus for:

figure

There is a button “Power On Monitors”

figure

And a logout button

figure

After a really long search, I found out that the credentials for the MQTT connection are located in the previously displayed clients and roles:

- *elfmonitor*
- WomblyC
- AlabasterS
- SiteDefaultPasswordRole
- *SiteElfMonitorRole*
- SiteAlabsterSAdminRole
- SiteWomblyCAdminRole

So the solution for Santa Vision B is:

figure figure

elfmonitor

If you talk to Ribb Bonbowford after that:

figure figure figure

This gives a new hint:

figure

To power on the monitors let’s use the password found in the roles:

figure

Connect As: elfmonitor

Password: SiteElfMonitorRole

Enter the IP from the Time Travel before in Camera Feed Server. This is also the IP from the MQTT server.

For Camera Feed Port look at the nmap output, it’s 9001 for the MQTT server.

You should now could read Monitors on. Connect to the broadcast feed

figure

Let’s connect to northpolefeeds as mentioned in the text to Santa Vision B:

figure

The picture now shown are as follows:

figure figure

figure figure figure figure figure

So this seems to be advertising for the two combating parts. What’s written in the next challenge of Santa Vision?

figure

Ok, then let’s do that! For that proceed the same way as above, click to “Connect to broadcast feed” and as value set frostbitfeed as Broadcast feed:

figure

This yields to output of frostbitfeed whose messages look like this:

Frostbite is a serious condition that can cause permanent damage to the body and/or network
Frostbit is a leading cause of network downtime
Frostbite can be prevented by using a firewall and keeping your network secure
While good backups are important, they won't prevent frostbite
To prevent frostbite, you should wear appropriate clothing and cover exposed skin and ports
Be sure everyone knows the signs and symptoms of frostbite
Frostbite can occur in as little as 30 minutes in extreme cold - faster in flat networks
Additional messages available in santafeed
Error msg: Unauthorized access attempt. /api/v1/frostbitadmin/bot/<botuuid>/deactivate, authHeader: X-API-Key, status: Invalid Key, alert: Warning, recipient: Wombley
Do you conduct regular frostbite preparedness exercises?
Let's Encrypt cert for api.frostbit.app verified. at path /etc/nginx/certs/api.frostbit.app.key

This gives a hint to another feed in on of the messages: santafeed.

Let’s look into that:

figure

Gathered this is:

Santa is making his list.
Santa is checking his list.
Santa role: superadmin
WombleyC role: admin
AlabasterS role: admin
Sixteen elves launched operation: Idemcerybu
singleAdminMode=false
Santa role: superadmin
superAdminMode=true
Santa is on his way to the North Pole

If you look very carefully you can read Sixteen elves launched operation: Idemcerybu. So the secret operation asked in for Santa Vision C is

Idemcerybu

figure figure

The next part of the challenge is:

figure

Perhaps talk to Ribb Bonbowford again:

figure figure

He says: “-send the right message, and Santa’s true spirit will return”. So we have to send something, which means in MQTT terms we have to publish something.

figure

figure figure