Scenario#
An IT employee at InfiniTech Solutions reported unusual activity associated with their email account. During the investigation, it was discovered that the employee’s work credentials had been exposed in a recent data breach.
The employee had used their work email address to sign up on a third-party platform, which exposed their credentials. Malicious actors exploited these credentials to access the employee’s email account and send phishing emails to other employees within the organization.
Your task is to investigate this incident by analyzing logs, determining the extent of the attack, identifying whether users interacted with the phishing emails, and uncovering the impact of the phishing campaign on the network.
Setup#
For this investigation scenario, we will primarily use Splunk Search to analyze the logs. Additionally, we’ll use other tools such as xml.onlineviewer, Cyberchef, and VirusTotal.
Reminders#
Splunk is an analysis platform that allows you to collect, index, and search data generated by systems, applications, and network devices. It retrieves its logs through various mechanisms such as agents installed on endpoints, log file ingestion, or even APIs.
Once collected, the data is indexed, which helps to organize and facilitate the search process. Splunk uses the SPL (Search Processing Language) to query this data.
Initial Access#
Question 1#
Which email account was compromised and used to launch the attack?
First, let’s check the available “sourcetypes” on our system:
index="main"
| stats count by sourcetype
Sourcetypes in Splunk are labels that indicate the type of data or the origin of the collected events.
To answer this question, we’ll focus on the “syslog” first. Syslogs gather logs generated by various devices and usually contain detailed information about system and security activities.
To speed things up and avoid being flooded with information, we’ll search with a regex to only display email addresses:
* sourcetype=syslog
| regex _raw="[\w\.-]+@[\w\.-]+\.\w+"
We see a lot of logs, so to make our task easier, we’ll count how many times each email address appears and display the results in a table:
* sourcetype=syslog
| rex field=_raw "(?<email>[\w\.-]+@[\w\.-]+\.\w+)"
| stats count by email
| sort -count
| head 10
| rename count AS "Number of occurrences", email AS "Email address"
We can even visualize this as a graph:
Answer: twhite@infinitechsolutions.xyz
Question 2#
After identifying the compromised account, the attacker sent phishing emails to other employees in the company. What are the names of these employees, listed in chronological order and separated by commas?
The email sending log format is as follows:
User [sender_email] [IP_address]; Message [message_ID] for [recipient_email]
For the search, we run:
* sourcetype=syslog
"User twhite@infinitechsolutions.xyz"
| rex field=_raw "Message <[^>]+> for (?<recipient>[\w\.-]+@[\w\.-]+\.\w+)"
| table _time recipient
| sort _time
| stats list(recipient) as recipients
| eval recipients=mvjoin(recipients, ", ")
This query allows us to identify and list the recipients in chronological order.
Indeed, we select the user from the compromised account and use a regex to extract and store the recipient’s email address in a variable.
Then, we sort the results chronologically and format the output for readability.
Answer: rnichols,llopez,gbaker,ahall
Question 3#
What is the name of the malicious attachment sent from the compromised account?
This question gave me more trouble than I expected. Initially, I searched:
* sourcetype=syslog
"User twhite@infinitechsolutions.xyz"
| regex _raw="for (rnichols|llopez|gbaker|ahall)@infinitechsolutions\.xyz"
However, there was no trace of any attachment. I then thought, “Hmm, I must not be on the right sourcetype; I’ll search the others.”
So I searched:
index=* "twhite@infinitechsolutions.xyz"
| stats count by sourcetype
Only syslog is available, so I’m on the right sourcetype. I then continued to think and decided, “I’ll bypass the formatting issue and simply search for file extensions.”
I asked ChatGPT for a list of extensions and ended up with this query:
index=* sourcetype=syslog
twhite@infinitechsolutions.xyz
| rex field=_raw "attachment=(?<malicious_attachment>[\w\.-]+\.(exe|zip|pdf|docx|xlsm|js|vbs|scr|bat))"
| table _time malicious_attachment
| sort _time
Still nothing, no attachment was found.
Since I was still stuck, I decided to think differently. The lab title is “Midnight RDP”, which reminded me of the APT known as “Midnight Blizzard”.
During my research, I came across an article titled “How Midnight Blizzard Uses Malicious .RDP Files to Exploit Windows Systems: A Technical Guide” (by Yua Mikanana).
Well, given the lab title, it was obvious. So I searched for “.RDP” and obtained the file:
Answer: cloud zerotrust compliance.rdp
Execution#
Question 1#
After analyzing user interactions, which employee downloaded and executed the malicious attachment?
We change the sourcetype, moving over to WinEventLog.
WinEventLog corresponds to Windows event logs, which record various system activities (like errors, warnings, security information, or application events) to allow tracking and analysis of events on a Windows system.
index=* sourcetype="xmlwineventlog"
cloud zerotrust compliance.rdp
Here, we can clearly see the username of the user who downloaded the file: C:\Users\rnichols\Downloads\cloud zerotrust compliance.rdp
Answer: rnichols
Question 2#
On the DC machine, a DLL tag was executed by the attacker in memory. What are the first 10 bytes of the SHA-256 hash of this malicious DLL file?
First, let’s list the DLLs related to PowerShell on the DC:
index=* sourcetype=xmlwineventlog
"Powershell"
| search host="DC01"
| rex field=_raw "(?<file>\b\w{6,8}\.\w{3}\b)"
| search file="*dll"
We notice that the complete script is too long to be recorded in a single event. It is therefore split into segments. This can be observed thanks to the MessageNumber
:
The two events share the same ActivityID {0B39225B-3C43-0001-5078-390B433CDB01}
as well as other identifiers, indicating that they belong to the same script execution.
The first segment (MessageNumber = 1
) contains the beginning of the script, including the configuration of the PowerShell environment and the definition of functions such as func_get_proc_address
and func_get_delegate_type
.
The last segment (MessageNumber = 33
) contains the end of the script, notably the part that decodes a Base64 string (XOR’d with the key 35) and allocates memory to execute the decoded code.
Together, these segments reassemble the entire payload which is likely intended to inject and execute code in memory.
Once reassembled, the script decodes an obfuscated payload (first Base64, then XOR’d with 35) and uses native Windows functions (like VirtualAlloc
via GetProcAddress
and GetDelegateForFunctionPointer
) to allocate memory and execute the malicious code.
[...]
$var [...] -bxor 35
[...]
We can reassemble it using a Splunk search:
index=* sourcetype=xmlwineventlog
EventID=4104
0B39225B-3C43-0001-5078-390B433CDB01
| stats values(ScriptBlockText) as Script by SystemTime
| table Script
We can then decode the script using Cyberchef:
We find our DLL in question:
Finally, we extract the file and retrieve its hash:
Answer: 0ee6bc20a7f855d881cce962de09c77960ea5c85ca013e3d123fce61109ff8c5
Persistence#
Question 1#
After the malicious connection was established, a file was dropped on the system. What is the name of this dropped file?
We know that the .rdp file was executed around 20:45:27 (see Question 1 from the Execution section).
So, we’ll start our search from that moment.
First, I tried searching in Splunk for downloads using PowerShell commands (Invoke-WebRequest, curl, etc.), but that didn’t yield anything.
I then modified my search:
index=*
sourcetype="xmlwineventlog"
"rnichols"
| rex field=_raw "CommandLine=(?<CommandLine>.*)"
| fillnull value=""
| search NOT CommandLine="</Data></EventData></Event>",""
| stats values(CommandLine) as CommandLine by _time, ParentCommandLine
| sort — _time
So why does this work?
Here are the event details:
The field TerminalSessionId=3
strongly suggests that the user was connected via an RDP session, because:
- Session 0 is often used by SYSTEM.
- Local interactive sessions usually start at 1.
- RDP sessions often have higher IDs (such as
2
,3
, etc.).
explorer.exe
is the main process of the Windows graphical interface and acts as a launcher for programs started by an interactive user.
Possible scenarios explaining this behavior:
Interactive session (RDP or not):
- If
rnichols
connected via RDP,explorer.exe
was launched as the user shell and executed the programs defined in Startup (ztssvc.exe
). - This situation can also occur even without RDP if the user logs in physically or through a service like
runas
.
- If
Automatic execution at user login:
- Since
ztssvc.exe
is in Startup, it is executed as soon as the user logs in (whether locally or via RDP).
- Since
Injection or Persistence via
explorer.exe
:- If an attacker persisted malware via Startup,
explorer.exe
will automatically run that binary when a user logs in.
- If an attacker persisted malware via Startup,
Exploit or forced execution by an attacker:
- If
explorer.exe
was compromised (for example, through DLL injection or another technique), it could have been used to executeztssvc.exe
.
- If
Answer: ztssvc.exe
Question 2#
To maintain long-term access, the attacker created a scheduled task on the compromised machine. What is the name of this task?
index=* sourcetype="xmlwineventlog"
EventCode=4698 OR EventCode=4702
| search user="rnichols"
| table _time EventCode TaskName Command User
| rename EventCode as "Event ID", TaskName as "Scheduled Task", Command as "Executed Command"
| sort _time
Question 3#
As part of their persistence strategy, the attacker created a new user account. What is the name of this unauthorized account?
Using the same query as in Question 1:
index=* sourcetype="xmlwineventlog"
"rnichols"
| rex field=_raw "CommandLine=(?<CommandLine>.*)"
| fillnull value=""
| search NOT CommandLine="</Data></EventData></Event>",""
| stats values(CommandLine) as CommandLine by _time, ParentCommandLine
| sort — _time
Within the results, you can find an encoded PowerShell command that looks very suspicious:
We take this command and decode it using Cyberchef:
Answer: Administrator
Question 4#
To facilitate remote access, the attacker modified the remote desktop settings. What is the name of the registry key that determines whether RDP (Remote Desktop Protocol) connections are allowed?
Having already investigated and administered quite a few Windows systems, I already knew the answer. Nevertheless, here’s how to find it:
We always search for PowerShell commands with -EncodedCommand
, as the attacker executed several:
Within these, you can see this command:
Answer: fDenyTSConnections
Question 5#
Further investigation revealed the creation of a new user account on the DC. What is the name of this account?
We can assume that this was done in the same way as on the other host. So we search with:
index=*
sourcetype="xmlwineventlog"
host="DC01"
"-EncodedCommand"
Answer: rniclos
Privilege Escalation#
Question 1#
When examining the privilege escalation technique, what are the last 6 bytes of the CLSID of the privileged COM interface that the attacker exploited?
The CLSID is a globally unique identifier (GUID) used by Windows COM (Component Object Model) to identify COM objects in the registry.
Attackers use CLSIDs of privileged COM interfaces to execute code with elevated privileges:
- They exploit misconfigured COM objects to escalate their privileges.
- An attacker can create, manipulate, or call a privileged COM interface* via
regsvr32.exe
,powershell
,mshta.exe
, orrundll32.exe
.
Here, we know that the malware is ztssvc.exe
. So we search for all EventCode “1” (process creation) events related to the execution of ztssvc.exe
and filter only those with an IntegrityLevel of “High”:
index=*
sourcetype="xmlwineventlog"
EventCode=1
| search Image="*ztssvc.exe*"
| search IntegrityLevel="High"
| table _time User CommandLine IntegrityLevel ParentCommandLine
| sort _time
Answer: 7CE93B6DC937
Question 2#
To elevate their privileges, the attacker dropped another file on the system. What is the name of this file?
We run the same command and review everything that happened after the download of the first file (ztssvc.exe
):
index=*
sourcetype="xmlwineventlog"
"rnichols"
| rex field=_raw "CommandLine=(?<CommandLine>.*)"
| fillnull value=""
| search NOT CommandLine="</Data></EventData></Event>",""
| search NOT ParentCommandLine=""
| stats values(CommandLine) as CommandLine by _time, ParentCommandLine
| sort — _time
Answer: Akagi64.exe
Defense Evasion#
Question 1#
The attacker attempted to modify the system behavior to weaken security settings. What is the name of the registry key that governs the User Account Control (UAC) prompt settings for administrative users?
The answer can be found in the encoded PowerShell commands we observed earlier:
Answer: ConsentPromptBehaviorAdmin
Question 2#
To avoid detection, the attacker moved the tag into a protected system directory. What is the name of this moved malicious file?
If a move occurred, there should be a file creation or renaming event (Event ID 11 or 2).
index=*
sourcetype=xmlwineventlog
EventCode=11 OR EventCode=2
"rnichols"
| search TargetFilename="C:\\Windows\\System32\\*" OR TargetFilename="C:\\Windows\\SysWOW64\\*" OR TargetFilename="C:\\ProgramData\\*"
| table _time User TargetFilename ProcessName
| sort _time
Answer: Amazon ZeroTrust Compl.exe
Discovery#
Question 1#
By determining the attacker’s first action on the compromised machine, what was the first command executed to gather system information?
Knowing that the machine was compromised at 20:45:27 (see Question 1 from the Execution section), we start our search from that time:
index=*
sourcetype="xmlwineventlog"
"rnichols"
| rex field=_raw "CommandLine=(?<CommandLine>.*)"
| fillnull value=""
| search NOT CommandLine="</Data></EventData></Event>",""
| search NOT ParentCommandLine=""
| stats values(CommandLine) as CommandLine by _time, ParentCommandLine
| sort — _time
Answer: whoami /groups
Lateral Movement#
Question 1#
Which tool did the attacker use to move laterally to the DC?
Once again, we need to investigate the encoded PowerShell commands, this time those issued by Amazon ZeroTrust Compl.exe
.
powershell -nop -exec bypass -EncodedCommand UwBlAHQALQBJAHQAZQBtACAAVwBTAE0AYQBuADoAXABsAG8AYwBhAGwAaABvAHMAdABcAEMAbABpAGUAbgB0AFwAVAByAHUAcwB0AGUAZABIAG8AcwB0AHMAIAAtAFYAYQBsAHUAZQAgACIARABDADAAMQAuAGEAZAAuAGkAbgBmAGkAbgBuAGkAdABlAGMAaABzAG8AbAB1AHQAZQBuAHMALgB4AHkAegAiACAALQBGAG8AcgBjAGUA
This command modifies the Windows Remote Management (WinRM) settings on IT01
to trust DC01.ad.infinitechsolutions.xyz
for remote connections. It’s a strong indicator of lateral movement via PowerShell Remoting (WinRM).
Answer: WinRM
Command & Control#
Question 1#
Tracing back the attacker’s activities, what was the IP address from which the malicious emails were sent?
We go back to the very first questions by searching for the user who sent the phishing emails:
index=*
sourcetype=syslog
"User twhite@infinitechsolutions.xyz"
Answer: 3.78.253.99
Question 2#
Since cloud zerotrust compliance.rdp
was executed on IT01
and we know the sender’s email IP address, we can directly test with:
index=* sourcetype=xmlwineventlog
(EventCode=1 OR EventCode=3)
| search host="IT01"
| search DestinationPort=3389 AND DestinationIp=3.78.253.99
| table _time User DestinationIp DestinationPort
| sort _time
Answer: 3.78.253.99:3389
Question 3#
Analysis revealed that the dropped file acts as a Cobalt Strike beacon. What is the endpoint of the command and control (C&C) server with which this beacon communicates?
First, let’s verify which file is the Cobalt Strike beacon:
Then, we continue the investigation by checking which IP/port Amazon ZeroTrust Compl.exe
connected to:
index=* sourcetype=xmlwineventlog
EventCode=3
| search Image="*Amazon ZeroTrust Compl.exe*"
| search NOT DestinationIp=""
| table _time User Image DestinationIp DestinationPort Protocol
| sort _time
(EventCode 3 corresponds to a network connection established by a process on a Windows machine)
Answer: 3.78.244.11:8080
Question 4#
By examining the DLL configuration, what value is associated with the ‘C2Server’ key that directs the beacon’s communication?
We return to VirusTotal and find this information in the “Behavior” tab.
Answer: 3.78.244.11,/dot.gif
Lab completed!