This post is a write-up or clues on how to resolve the KC7 investigation case of SANS Holiday Hack Challenge 2024 The Great Elf Conflict . You can use it as a helpful guide when encountering an obstacle or trying to understand a question. Different ways to answer questions might exist, so feel free to explore your path.

Section 1: KQL 101

You got it šŸ‘¾

Section 2: Section 2: Operation Surrender Alabaster’s Espionage

Question 1:

surrender

Question 2: Who was the sender of the phishing email that set this plan into motion?

The question had the hind, which is surrender. Using that keyword in our search, we can identify the sender’s email address in the phishing attack.

Email
| where subject contains "surrender"
| distinct sender

Question 3: What was the filename of the document that Team Alabaster distributed in their phishing email?

Email
| where subject contains "surrender"
| distinct recipient
| count 

Question 4: What was the filename of the document that Team Alabaster distributed in their phishing email?

Email
| where subject contains "surrender"
| extend FilePath = (parse_url(link).Path)
| extend FileName = tostring(split(FilePath, "/")[-1])
| distinct link, FileName

Line 3: | extend FilePath = (parse_url(link).Path)

This line creates a new column called FilePath. The parse_url(link) function is used to break down the link field into its components. The .Path property retrieves the path part of the URL. The result is stored in the FilePath column.

Line 4: | extend FileName = tostring(split(FilePath, "/")[-1])

This line creates another new column called FileName. Here’s what happens in this line:

  • split(FilePath, "/") splits the FilePath string into an array of substrings using the / character as the delimiter. For example, if FilePath is /public/share/images/Team_Wombley_Surrender.doc, the result of the split would be an array like ["", "public", "share", "images", "Team_Wombley_Surrender.doc"].
  • [-1] accesses the last element of that array, which is the file name (Team_Wombley_Surrender.doc in this case).
  • tostring(...) ensures that the result is explicitly cast to a string type, which is important for further operations like distinct.

Question 5: Who was the first person from Team Wombley to click the URL in the phishing email?

You got it šŸ‘¾

Question 6: What was the filename that was created after the .doc was downloaded and executed?

First, find the timestamp when the .doc file was downloaded to Joyelle Tinseltoe’s host.

ProcessEvents
| where process_commandline contains "Team_Wombley_Surrender.doc"
| where hostname == "Elf-Lap-W-Tinseltoe"

Then, find all events after the timestamp.

ProcessEvents
| where timestamp >= datetime(2024-11-27T14:12:44Z)
| where hostname == "Elf-Lap-W-Tinseltoe"
| project process_commandline

Question 7: Enter your flag to continue

You got it šŸ‘¾

Section 3: Operation Snowfall: Team Wombley’s Ransomware Raid

Question 1:

snowfall

Question 2: What was the IP address associated with the password spray?

You got it šŸ‘¾ Hit: IP address with the top failed login attempts.

Question 3: How many unique accounts were impacted where there was a successful login from 59.171.58.12?

The question provided us with the activities of interest, and we can confirm which strings to filter in the result column by getting a distinct list of the logged activities. Ā 

AuthenticationEvents
| distinct result

Let’s put the a missing pieces together.

AuthenticationEvents
| where src_ip == "59.171.58.12"
| where result == "Successful Login"
| distinct username
| count 

Question 4: What service was used to access these accounts/devices?

You got it šŸ‘¾ Hint: Did you look at the image? Did you check the description?

AuthenticationEvents
| where src_ip == "59.171.58.12"
| where result == "Successful Login"

Question 5: What file was exfiltrated from Alabasterā€™s laptop?

The question gave us a hint about checking the ProcessEvents table. Let’s take a look at the information in the table.

ProcessEvents
| take 10

Okay! We will need the hostname and the username by analyzing the logs.

First, let’s find Alabasterā€™s information from the Employees table.

Employees
| where name contains "Alabaster"
  • hostname: Elf-Lap-A-Snowball
  • username: alsnowball

Then, let’s check what logs we recorded for the hostname.

ProcessEvents
| where hostname has "Elf-Lap-A-Snowball"

The results show 25 activities which could be investigated easily. However, the instructions recommend using a timestamp to filter the results.

The IP address associated with the password spray is 59.171.58.12, and the victim username is identified as snowball.

AuthenticationEvents
| where src_ip == "59.171.58.12"
| where result == "Successful Login"
| where username == "alsnowball"

Perfect! We have our time stamp and it is time to analyze the log activities in the process_commandline.

ProcessEvents
| where hostname has "Elf-Lap-A-Snowball"
| where timestamp > datetime(2024-12-11T01:39:50Z)

Question 6: What is the name of the malicious file that was run on Alabaster’s laptop?

Using the query we built from question 5 and investigating the activities in the process_commandline, we can locate the command line executing the ransomware.

ProcessEvents
| where hostname has "Elf-Lap-A-Snowball"
| where timestamp > datetime(2024-12-11T01:39:50Z)

Question 7: Enter your flag to continue

You got it šŸ‘¾

Section 4: Echoes in the Frost: Tracking the Unknown Threat

Question 1:

stay frosty

Question 2: What was the timestamp of first phishing email about the breached credentials received by Noel Boetie?

Let’s find Noel Boetie from the Employees table.

Employees
| where name contains "Noel Boetie"

Let’s find the email.

Email
| where recipient == "noel_boetie@santaworkshopgeeseislands.org"
| where subject contains "credentials"
| top 1 by timestamp asc

This activity should be logged in the OutboundNetworkEvents table. By doing take 10, we will identify the columns we can filter on to locate the timestmap of the activity.

OutboundNetworkEvents
| take 10

Okay, we can use the url and filter our results.

OutboundNetworkEvents
| where url contains "https://holidaybargainhunt.io/published/files/files/echo.exe"

Question 4: What was the IP for the domain where the file was hosted?

The list of the available tables shows a table named PassiveDns.

PassiveDns
| where domain contains "holidaybargainhunt.io"

Question 5: 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?

The AuthenticationEvents table will log this activity. By running this query, we can identify the hostname.

AuthenticationEvents
| where src_ip == "182.56.23.122"

Question 6: What was the script that was run to obtain credentials?

ProcessEvents
| where hostname contains "WebApp-ElvesWorkshop"

Hint: Invoke-********.ps1

Question 7: What is the timestamp where Noel executed the file?

In question 3, we found out when Noel clicked the link to download the file. Let’s get the ‘ProcessEvents’ activities after that time.

ProcessEvents
| where hostname contains "Elf-Lap-A-Boetie"
| where timestamp > datetime(2024-12-12T15:13:55Z)

Question 8: What domain was the holidaycandy.hta file downloaded from?

OutboundNetworkEvents
| where url contains "holidaycandy.hta"

Question 9: what was the first file that was created after extraction?

Starting from our query in question 7 start analyzing process_commandline to identify the file.

ProcessEvents
| where hostname contains "Elf-Lap-A-Boetie"
| where timestamp > datetime(2024-12-12T15:13:55Z)

Question 10: What is the name of the property assigned to the new registry key?

Continue analyzing the results from question 9.

Question 11: To obtain your FINAL flag use the KQL below with your last answer!

You got it šŸ‘¾