Module 6: Detecting Windows Attacks with Splunk

These notes are from the practical Splunk hunting sections in the module. Each section keeps the lab SPL as written, plus a short note on what the hunt is looking for and what showed up when I ran it.
Back to Module 4 fundamentals: Module 4: Understanding Log Sources & Investigating with Splunk
Module 6 Navigation
On this page
- Introduction
- Windows event log hunts
- 1. Detecting Common User/Domain Recon
- 2. Detecting Password Spraying
- 3. Detecting Responder-like Attacks
- 4. Detecting Kerberoasting/AS-REPRoasting
- 5. Detecting Pass-the-Hash
- 6. Detecting Pass-the-Ticket
- 7. Detecting Overpass-the-Hash
- 8. Detecting Golden Ticket and Silver Ticket Attacks
- 9. Detecting Delegation and Constrained Delegation Attacks
- 10. Detecting DCSync and DCShadow
- Zeek network hunts
- 11. Detecting RDP Brute Force
- 12. Detecting Beaconing Malware
- 13. Detecting Nmap Port Scanning
- 14. Detecting Kerberos Brute Force Attacks
- 15. Detecting Kerberoasting (Network)
- 16. Detecting Golden Tickets
- 17. Detecting Cobalt Strike PsExec
- 18. Detecting Zerologon
- 19. Detecting HTTP Exfiltration
- 20. Detecting DNS Exfiltration
- 21. Detecting Ransomware
- Key Takeaway
- References
Skill Assessment
Introduction
This module was query-heavy. Most sections follow the same pattern: what the attack is, which logs to use, the lab SPL, and what stood out in the lab data.
Windows Event Log Hunts
1. Detecting Common User/Domain Recon
1. What is Active Directory Reconnaissance? Active Directory (AD) domain reconnaissance is the phase where attackers gather information about a target environment’s architecture, users, and groups to pinpoint high-value targets, escalate privileges, and move laterally. Attackers typically do this in two distinct ways:
-
Native Windows commands: Utilizing built-in system executables like
whoami,net group, ornltest. -
Automated tools: Deploying tools like BloodHound or SharpHound, which leverage graph theory to map network relationships by spamming the Domain Controller with a high volume of LDAP queries.
2. How do you detect it? Because attackers use two different methodologies, you need two separate log sources to catch them:
-
For native commands: You must monitor process creation logs, typically using Sysmon (Event ID 1).
-
For BloodHound: Default Windows Event Logs do not natively record LDAP queries well (even with Event 1644 enabled, you will miss a lot). To catch this, you must utilize an ETW wrapper tool like Silk Service to capture the
Microsoft-Windows-LDAP-Clientlogs.
3. The Splunk Queries & Logic (Note: You may need to adjust or remove the earliest and latest timestamps in these examples, as they are currently filtering for a specific timeframe.)
Detecting Native Tools:
index=main source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventID=1 earliest=1690447949 latest=1690450687
| search process_name IN (arp.exe,chcp.com,ipconfig.exe,net.exe,net1.exe,nltest.exe,ping.exe,systeminfo.exe,whoami.exe) OR (process_name IN (cmd.exe,powershell.exe) AND process IN (*arp*,*chcp*,*ipconfig*,*net*,*net1*,*nltest*,*ping*,*systeminfo*,*whoami*))
| stats values(process) as process, min(_time) as _time by parent_process, parent_process_id, dest, user
| where mvcount(process) > 3
How it works:
-
EventID=1: Tells Splunk to only look at process creation events logged by Sysmon. -
search process_name IN (...): Filters the logs to look for specific reconnaissance commands (likearp.exe,net.exe, orwhoami.exe). -
stats ... by parent_process: Groups the results based on the parent process that originally launched the commands. -
where mvcount(process) > 3: The trigger, it alerts you if a single parent process spawns more than three of these distinct recon tools.
Detecting BloodHound:
index=main earliest=1690195896 latest=1690285475 source="WinEventLog:SilkService-Log"
| spath input=Message
| rename XmlEventData.* as *
| table _time, ComputerName, ProcessName, ProcessId, DistinguishedName, SearchFilter
| sort 0 _time
| search SearchFilter=" *(samAccountType=805306368)* "
| stats min(_time) as _time, max(_time) as maxTime, count, values(SearchFilter) as SearchFilter by ComputerName, ProcessName, ProcessId
| where count > 10
| convert ctime(maxTime)
How it works:
-
source="WinEventLog:SilkService-Log": Instructs Splunk to pull the specialized LDAP logs captured by the SilkService ETW wrapper. -
spath input=Message: Automatically extracts the hidden structured data (like XML) into readable fields. -
search SearchFilter="...": Looks for the specific LDAP search filters commonly used by reconnaissance tools, such as*(samAccountType=805306368)*. -
stats ... by ProcessId: Groups the logs by the exact process making the queries. -
where count > 10: The trigger, it alerts you if a single process fires off more than 10 of these suspicious LDAP queries.
2. Detecting Password Spraying
1. What is Password Spraying? Unlike traditional brute-force attacks that try many passwords against a single account, password spraying distributes the attack across multiple different accounts using a limited set of commonly used passwords. The primary goal is to evade standard account lockout policies (which trigger after a few failed attempts on a single account) and make the attack less noticeable.
2. How do you detect it? To detect password spraying, you need to monitor Windows Security Event Logs for patterns of multiple failed logons across different user accounts that originate from the same source IP address within a short time frame.
-
Primary Log Source: Event ID 4625 (Failed Logon).
-
Secondary Log Sources: You can also monitor Kerberos and NTLM failures using Event IDs like 4768, 4776, 4648, and 4771.
3. The Splunk Query & Logic (Note: You may need to adjust or remove the earliest and latest timestamps in this example, as they are currently filtering for a specific timeframe.)
index=main earliest=1690280680 latest=1690289489 source="WinEventLog:Security" EventCode=4625
| bin span=15m _time
| stats values(user) as Users, dc(user) as dc_user by src, Source_Network_Address, dest, EventCode, Failure_Reason
How it works:
-
EventCode=4625: Tells Splunk to specifically look at failed logon attempts logged in the Windows Security Event Log. -
bin span=15m _time: Groups the logon events into 15-minute intervals to help spot sudden spikes or trends over a short period. -
stats ... by src, Source_Network_Address, dest: Groups the results based on where the attack is coming from (source IP) and where it is aimed. -
dc(user) as dc_user: Calculates the distinct count of unique user accounts targeted. Finding a high number of distinct users failing to log in from the same source IP in a 15-minute window is the core indicator of a password spraying attack.
3. Detecting Responder-like Attacks
1. What is LLMNR/NBT-NS Poisoning?
LLMNR and NBT-NS are backup name resolution protocols used when standard DNS fails to find a hostname. Attackers, commonly utilizing a tool called Responder, exploit the lack of security in these protocols by listening for broadcast queries looking for mistyped or non-existent network shares. When a victim searches for a bad hostname, the attacker’s machine responds pretending to be the target, which forces the victim to connect to them. The ultimate goal is to steal the victim’s NetNTLM hash so the attacker can crack it or relay it for unauthorized access.
2. How do you detect it?
Detecting this activity requires monitoring for unusual name resolution traffic or rogue responses. You can use three log sources:
-
Custom Honeypot Logs: You can run a scheduled PowerShell script that queries a fake hostname; if it receives a resolution response, an attacker is actively spoofing on the network. This activity can be written to a custom Windows Event Log (e.g.,
LLMNRDetection). -
Sysmon (Event ID 22): This logs DNS queries, which can be monitored for requests associated with non-existent or mistyped file shares.
-
Windows Security Logs (Event ID 4648): This tracks explicit logons and can catch users attempting to authenticate to rogue file shares set up by the attacker.
3. The Splunk Queries & Logic
(Note: As with previous examples, you should adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment.)
Detecting with Custom Honeypot Logs:
index=main earliest=1690290078 latest=1690291207 SourceName=LLMNRDetection
| table _time, ComputerName, SourceName, Message
How it works:
-
SourceName=LLMNRDetection: Instructs Splunk to specifically pull events generated by your custom PowerShell honeypot script. -
table ... Message: Formats the output into a clean table showing when the spoofed response occurred, the affected computer, and the message containing the attacker’s IP address.
Detecting Mistyped Shares (Sysmon):
index=main earliest=1690290078 latest=1690291207 EventCode=22
| table _time, Computer, user, Image, QueryName, QueryResults
How it works:
-
EventCode=22: Filters the logs for Sysmon DNS Query events. -
table ... QueryName, QueryResults: Displays the exact process making the request, the mistyped hostname (QueryName), and the specific IP address returning the malicious spoofed result (QueryResults).
Detecting Rogue Logons:
index=main earliest=1690290814 latest=1690291207 EventCode IN (4648)
| table _time, EventCode, source, name, user, Target_Server_Name, Message
| sort 0 _time
How it works:
-
EventCode IN (4648): Tells Splunk to look for explicit logon attempts within the Windows Security event logs. -
table ... Target_Server_Name: Highlights the specific destination server the victim is trying to connect to, allowing you to spot logons being sent to an untrusted, attacker-controlled system.
4. Detecting Kerberoasting/AS-REPRoasting
1. What are Kerberoasting and AS-REProasting?
Both are Active Directory attacks aimed at extracting password hashes for offline cracking, but they target different accounts:
-
Kerberoasting: Targets AD service accounts. Attackers exploit weak passwords by requesting a Kerberos Service Ticket (TGS) for a service account, extracting it, and brute-forcing the hash offline.
-
AS-REProasting: Targets user accounts that have “Kerberos pre-authentication” disabled. Normally, pre-authentication requires a user to prove their identity before getting a Ticket Granting Ticket (TGT). If disabled, an attacker can request a TGT for that user without knowing the password and crack the returned hash offline.
2. How do you detect them?
-
For Kerberoasting: Normal behavior involves a user requesting a service ticket (Event 4769) and then actually logging into the service (Event 4648). Attackers, however, just request the ticket and leave. You detect this by looking for ticket requests without a corresponding logon event.
-
For AS-REProasting: You must monitor Windows Security logs for TGT requests (Event 4768) where the pre-authentication type is explicitly logged as disabled (Type 0).
3. The Splunk Queries & Logic
(Note: Adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment.)
Detecting Kerberoasting (Ticket Requests without Logons):
index=main earliest=1690450374 latest=1690450483 EventCode=4648 OR (EventCode=4769 AND service_name=iis_svc)
| dedup RecordNumber
| rex field=user "(?<username>[^@]+)"
| search username!=*$
| transaction username keepevicted=true maxspan=5s endswith=(EventCode=4648) startswith=(EventCode=4769)
| where closed_txn=0 AND EventCode = 4769
| table _time, EventCode, service_name, username
How it works:
-
EventCode=4648 OR EventCode=4769: Tells Splunk to pull both service ticket requests (4769) and explicit logon events (4648). -
transaction ... maxspan=5s: Groups these events together by the username if they happen within a 5-second window. -
startswith=(EventCode=4769) endswith=(EventCode=4648): Defines a complete, benign transaction (requesting a ticket, then logging in). -
where closed_txn=0 AND EventCode = 4769: The trigger, it alerts you to “open” transactions where a ticket was requested but the user never logged in.
Detecting AS-REProasting (Disabled Pre-Authentication):
index=main earliest=1690392745 latest=1690393283 source="WinEventLog:Security" EventCode=4768 Pre_Authentication_Type=0
| rex field=src_ip "(::ffff:)?(?<src_ip>[0-9.]+)"
| table _time, src_ip, user, Pre_Authentication_Type, Ticket_Options, Ticket_Encryption_Type
How it works:
-
EventCode=4768: Filters the logs specifically for Kerberos TGT requests. -
Pre_Authentication_Type=0: The trigger, it immediately flags any ticket requests made for an account where pre-authentication is disabled, indicating a potential AS-REProasting attempt. -
rex field=src_ip...: A helpful formatting command that extracts the true IPv4 address of the attacker so you know where the request came from.
5. Detecting Pass-the-Hash
1. What is a Pass-the-Hash Attack?
Pass-the-Hash (PtH) is a technique where attackers with local administrative privileges extract a user’s NTLM password hash directly from system memory, usually employing tools like Mimikatz. Instead of needing the victim’s plaintext password, the attacker reuses (or “passes”) this hash to authenticate and move laterally across the network.
2. How do you detect it?
When an attacker uses an alternate credential for remote access, it generates a specific Windows Security logon event (Event ID 4624 with Logon Type 9, meaning “NewCredentials”). However, searching only for this event can trigger false positives from legitimate system administration.
To accurately detect a PtH attack, you must correlate that logon event with the attacker’s tool physically accessing the credential memory. You detect this by linking the Logon Type 9 event with a Sysmon Process Access event (Event ID 10) specifically targeting the lsass.exe process.
3. The Splunk Query & Logic
(Note: Adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment.)
index=main earliest=1690450689 latest=1690451116 (source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=10 TargetImage="C:\Windows\system32\lsass.exe" SourceImage!="C:\ProgramData\Microsoft\Windows Defender\platform\*\MsMpEng.exe") OR (source="WinEventLog:Security" EventCode=4624 Logon_Type=9 Logon_Process=seclogo)
| sort _time, RecordNumber
| transaction host maxspan=1m endswith=(EventCode=4624) startswith=(EventCode=10)
| stats count by _time, Computer, SourceImage, SourceProcessId, Network_Account_Domain, Network_Account_Name, Logon_Type, Logon_Process
| fields - count
How it works:
-
EventCode=10 TargetImage="...\lsass.exe": Tells Splunk to look for processes accessing the LSASS memory, while specifically filtering out known safe programs like Windows Defender to reduce noise. -
EventCode=4624 Logon_Type=9: Tells Splunk to also look for “NewCredentials” logon events originating from theseclogoprocess. -
transaction ... maxspan=1m: The core trigger, it links the two behaviors together. It flags an alert if a process accesses LSASS memory and a Logon Type 9 event occurs on the exact same host within a 1-minute window.
What is seclogo? It’s the Secondary Logon service (seclogon, truncated in the event field), the mechanism behind runas and CreateProcessWithLogonW. When a process launches with alternate credentials, Windows logs it as Logon Type 9 (NewCredentials) through this service. It doesn’t validate the credential immediately - it caches it and only uses it once the process reaches out over the network. That’s exactly what Mimikatz’s sekurlsa::pth does: spawn a process via this same mechanism, handing it a stolen hash instead of a password.
6. Detecting Pass-the-Ticket
1. What is a Pass-the-Ticket (PtT) Attack?
Pass-the-Ticket is a lateral movement technique where an attacker extracts valid Kerberos tickets, either a Ticket Granting Ticket (TGT) or a Ticket Granting Service (TGS) ticket, from a compromised system’s memory using tools like Mimikatz or Rubeus. Instead of needing the user’s plaintext password or NTLM hash, the attacker injects these stolen tickets into their current session to securely authenticate and access other network resources.
2. How do you detect it?
Normal Kerberos authentication follows a strict sequence: a user must first request a TGT from the Domain Controller (Event ID 4768), and then use that TGT to request a Service Ticket for a specific resource (Event ID 4769).
When an attacker imports a stolen TGT into their session, the Domain Controller never sees the initial request originating from the attacker’s system. Therefore, you detect PtT by hunting for “partial” Kerberos authentication flows, specifically, looking for Service Ticket requests (Event 4769) that lack a preceding TGT request (Event 4768) from the same system within a standard timeframe.
3. The Splunk Query & Logic
(Note: You may need to adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment.)
index=main earliest=1690392405 latest=1690451745 source="WinEventLog:Security" user!=*$ EventCode IN (4768,4769,4770)
| rex field=user "(?<username>[^@]+)"
| rex field=src_ip "(\:\:ffff\:)?(?<src_ip_4>[0-9\.]+)"
| transaction username, src_ip_4 maxspan=10h keepevicted=true startswith=(EventCode=4768)
| where closed_txn=0
| search NOT user="*@*"
| table _time, ComputerName, username, src_ip_4, service_name, category
How it works:
-
EventCode IN (4768,4769,4770): Instructs Splunk to pull all TGT requests (4768), Service Ticket requests (4769), and Ticket renewals (4770) from the Windows Security logs. -
rex field=...: These two commands use regular expressions to normalize the data. They strip domain suffixes to extract the exact username, and format all IP addresses (even those logged as IPv6) into standard IPv4 formats so they can be tracked accurately. -
transaction ... startswith=(EventCode=4768): This groups the Kerberos events together based on the user and source IP over a 10-hour window (maxspan=10h). It defines a “normal” transaction as one that properly starts with a TGT request (startswith=4768). -
where closed_txn=0(combined withkeepevicted=true): This is the core trigger. Because we told the transaction to keep “evicted” (orphaned) events, filtering forclosed_txn=0specifically isolates incomplete Kerberos flows. It flags the anomalous events, like a 4769 Service Ticket request, that occurred without the required starting 4768 TGT request.
Scaling note: transaction over high event volume is expensive, and multi-DC/NAT environments will generate “orphaned” tickets from infrastructure alone, not just attackers - at enterprise scale this needs a stats-based correlation (or a LogonId-based join) instead of transaction.
7. Detecting Overpass-the-Hash
1. What is an Overpass-the-Hash Attack?
Overpass-the-Hash (also known as Pass-the-Key) is a technique where attackers use a stolen NTLM password hash or AES key to fraudulently request a legitimate Kerberos Ticket Granting Ticket (TGT). Unlike a standard Pass-the-Hash attack which relies on NTLM authentication, this technique upgrades the attacker’s access to Kerberos. Attackers typically use Mimikatz to extract the user’s hash from system memory (requiring local admin rights) and then use a tool like Rubeus to craft the raw ticket request to the Domain Controller.
2. How do you detect it?
If an attacker uses Mimikatz to perform this attack, it leaves the exact same memory-access artifacts as a standard Pass-the-Hash attack and can be detected using the same logic.
However, if they use Rubeus, standard detections may fail. Rubeus sends its ticket request directly to the Domain Controller, generating a standard TGT request log (Event ID 4768). The key to detecting this is monitoring network connections: legitimate Kerberos traffic to the Domain Controller should only originate from the system’s Local Security Authority process (lsass.exe). You can detect Rubeus by hunting for network traffic aimed at the Kerberos port (Port 88) originating from any other unusual process.
3. The Splunk Query & Logic
(Note: Adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment.)
index=main earliest=1690443407 latest=1690443544 source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" (EventCode=3 dest_port=88 Image!=*lsass.exe) OR EventCode=1
| eventstats values(process) as process by process_id
| where EventCode=3
| stats count by _time, Computer, dest_ip, dest_port, Image, process
| fields - count
How it works:
-
EventCode=3 dest_port=88 Image!=*lsass.exe: This is the core detection trigger. It tells Splunk to search Sysmon logs for Network Connection events (EventCode=3) aimed at the Kerberos port (Port 88), but specifically excludes the legitimatelsass.exeprocess. -
OR EventCode=1: It simultaneously pulls Sysmon Process Creation events (EventCode=1) so it can grab the full command-line details of the malicious tool. -
eventstats ... by process_id: This command links the network connection event with the process creation event using the Process ID, allowing you to see exactly which suspicious executable (like Rubeus) made the unauthorized Kerberos request. -
where EventCode=3 | stats ...: Finally, it filters the results to only show the malicious network connections and formats them into a clean table displaying the time, computer, destination IP, and the rogue process.
8. Detecting Golden Ticket and Silver Ticket Attacks
1. What are Golden and Silver Ticket Attacks?
Both are advanced Kerberos forging techniques where an attacker creates their own authentication tickets to gain unauthorized access, but they differ in scope and target:
-
Golden Ticket: An attacker extracts the NTLM hash of the Domain Controller’s
KRBTGTaccount (often using a DCSync attack or LSASS memory dump). With this hash, they can forge a Ticket Granting Ticket (TGT), granting themselves highly persistent, domain administrator privileges across the entire environment. -
Silver Ticket: Instead of the
KRBTGTaccount, the attacker targets the password hash of a specific service account (such as SharePoint, MSSQL, or CIFS). They use this to forge a Ticket Granting Service (TGS) ticket. While this only grants access to that specific target service, it still allows the attacker to impersonate any user on that resource.
2. How do you detect them?
Because the actual forging of these tickets happens offline, detecting the creation process is virtually impossible without catching the initial credential dumping. Instead, you must monitor for how these tickets are used:
-
For Golden Tickets: A forged Golden Ticket is ultimately injected into a session just like a Pass-the-Ticket attack. Therefore, you can use the exact same logic, hunting for Service Ticket requests that lack a corresponding TGT request.
-
For Silver Tickets: Attackers often generate Silver Tickets for arbitrary or entirely non-existent users. You can detect this by cross-referencing successful logons (Event ID 4624) against a list of legitimate, actually created users (Event ID 4720). Additionally, because these forged tickets grant administrative rights without standard validation, hunting for newly assigned “Special Logons” (Event ID 4672) can reveal the anomaly.
3. The Splunk Queries & Logic
(Note: As always, you will want to adjust or remove the static timestamps like latest=1690545656 for your live environment).
Detecting Golden Tickets (The Pass-the-Ticket Approach):
index=main earliest=1690451977 latest=1690452262 source="WinEventLog:Security" user!=*$ EventCode IN (4768,4769,4770)
| rex field=user "(?<username>[^@]+)"
| rex field=src_ip "(\:\:ffff\:)?(?<src_ip_4>[0-9\.]+)"
| transaction username, src_ip_4 maxspan=10h keepevicted=true startswith=(EventCode=4768)
| where closed_txn=0
| search NOT user="*@*"
| table _time, ComputerName, username, src_ip_4, service_name, category
How it works:
- This uses the exact same logic as Pass-the-Ticket detection. It groups Kerberos events into transactions and flags “open” flows (
where closed_txn=0) where an attacker has injected a forged Golden Ticket into a session without a legitimate, initial TGT request being logged by the Domain Controller.
Detecting Silver Tickets (Hunting for Phantom Users):
(Prerequisite: You must first create a lookup table of valid users by running a background search for EventCode=4720 and outputting it to users.csv).
index=main latest=1690545656 EventCode=4624
| stats min(_time) as firstTime, values(ComputerName) as ComputerName, values(EventCode) as EventCode by user
| eval last24h = 1690451977
| where firstTime > last24h
| eval last24h=relative_time(now(),"-24h@h")
| convert ctime(firstTime)
| convert ctime(last24h)
| lookup users.csv user as user OUTPUT EventCode as Events
| where isnull(Events)
How it works:
-
EventCode=4624: Pulls successful logons from the Windows Security logs. -
eval last24h.../where firstTime > last24h: Sets a timeframe threshold to only evaluate recent logons. -
lookup users.csv ... OUTPUT EventCode as Events: Takes the user attempting to log in and checks them against your predefined CSV list of legitimate users. -
where isnull(Events): The core trigger, it alerts you if the user successfully logging into the service does not exist in your legitimate user database, strongly indicating a forged Silver Ticket.
Detecting Silver Tickets (Anomalous Special Privileges):
index=main latest=1690545656 EventCode=4672
| stats min(_time) as firstTime, values(ComputerName) as ComputerName by Account_Name
| eval last24h = 1690451977
| eval last24h=relative_time(now(),"-24h@h")
| where firstTime > last24h
| table firstTime, ComputerName, Account_Name
| convert ctime(firstTime)
How it works:
-
EventCode=4672: Filters specifically for “Special Logon” events, which trigger when an account with administrative equivalence logs in. -
stats min(_time) ... where firstTime > last24h: Groups these privileged logons and isolates any new instances occurring within your defined recent timeframe, allowing you to manually review if that account should actually have those rights.
9. Detecting Delegation and Constrained Delegation Attacks
1. What are Active Directory Delegation Attacks?
Delegation is a feature allowing a service or computer to authenticate to another resource on behalf of a user. Attackers exploit this to impersonate high-privileged accounts, typically targeting two distinct configurations:
-
Unconstrained Delegation: A highly permissive, older setting where the Domain Controller embeds the user’s actual Ticket Granting Ticket (TGT) into the service ticket. If an attacker compromises a server with this enabled, they can extract the TGTs of anyone who connects to it (using tools like Mimikatz) and completely hijack their identity.
-
Constrained Delegation: A safer alternative restricting delegation only to specific services (defined in the
msDS-AllowedToDelegateToproperty). However, attackers can exploit Kerberos Protocol Extensions (specifically S4U2self and S4U2proxy) to forge a ticket and impersonate an administrator on those specific permitted services.
2. How do you detect them?
Because the actual ticket theft often happens directly in memory, the most reliable way to catch delegation attacks is by monitoring the initial discovery phase and the anomalous network traffic generated by the exploitation tools.
-
Detecting Discovery: To find vulnerable accounts, attackers run LDAP queries. You must monitor PowerShell Script Block Logging (Event ID 4104) to catch them querying the specific delegation properties.
-
Detecting Execution (Constrained): Tools like Rubeus must connect directly to the Domain Controller to request the forged S4U impersonation tickets. You detect this by monitoring Sysmon Network Connections (Event ID 3) aimed at the Kerberos port (Port 88) originating from unusual processes.
(Note: Because Unconstrained Delegation relies on reusing stolen tickets, you can also detect its execution using standard Pass-the-Ticket logic).
3. The Splunk Queries & Logic
(Note: Adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment).
Detecting Delegation Discovery (PowerShell):
index=main earliest=1690544538 latest=1690562556 source="WinEventLog:Microsoft-Windows-PowerShell/Operational" EventCode=4104 Message=" *TrustedForDelegation* " OR Message=" *userAccountControl:1.2.840.113556.1.4.803:=524288* " OR Message=" *msDS-AllowedToDelegateTo* "
| table _time, ComputerName, EventCode, Message
How it works:
-
EventCode=4104: Tells Splunk to analyze PowerShell Script Block logs, which record the actual code executed by a user or attacker on the system. -
Message=" *TrustedForDelegation* " OR ...: The core trigger, it searches the executed scripts for the specific LDAP property names attackers look for when hunting down both unconstrained and constrained delegation targets.
Detecting Constrained Execution (Anomalous Kerberos Traffic):
index=main earliest=1690562367 latest=1690562556 source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational"
| eventstats values(process) as process by process_id
| where EventCode=3 AND dest_port=88
| table _time, Computer, dest_ip, dest_port, Image, process
How it works:
-
source="...Sysmon...": Instructs Splunk to pull Sysmon operational logs. -
EventCode=3 AND dest_port=88: Filters for network connection events (Event 3) specifically communicating over Port 88, the default Kerberos port used by the Domain Controller. -
eventstats ... by process_id: Links the network connection data with the process creation data. This allows you to spot unauthorized, non-standard executables (like Rubeus) attempting to manually request Kerberos tickets via S4U extensions.
10. Detecting DCSync and DCShadow
1. What are DCSync and DCShadow Attacks?
Both attacks abuse Active Directory replication permissions to either steal data or maliciously modify the environment:
-
DCSync: An attacker mimics a legitimate Domain Controller using tools like Mimikatz to request domain replication data (specifically using the
DRSGetNCChangesinterface). This allows them to extract sensitive password hashes, including high-value accounts like KRBTGT, to craft Golden Tickets or execute Pass-the-Hash attacks. -
DCShadow: A highly stealthy tactic where an attacker registers a rogue Domain Controller to push unauthorized changes to Active Directory objects (such as quietly adding an account to Domain Admins) without generating standard security logs. It then initiates replication to push these changes throughout the legitimate domain.
2. How do you detect them?
-
For DCSync: You must monitor for unauthorized “DS-Replication-Get-Changes” operations. This requires enabling a specific Advanced Audit Policy (DS Access) so Windows generates Event ID 4662 when these replication requests occur.
-
For DCShadow: To successfully register a rogue Domain Controller, the attacker must append a global catalog ServicePrincipalName (SPN) to a computer object. You can detect this anomaly by monitoring Event ID 4742, which logs when a computer account is changed.
3. The Splunk Queries & Logic
(Note: As with previous sections, adjust or remove the earliest and latest timestamps depending on the timeframe you want to search in your environment).
Detecting DCSync (Unauthorized Replication Requests):
index=main earliest=1690544278 latest=1690544280 EventCode=4662 Message=" *Replicating Directory Changes* "
| rex field=Message "(?P<property>Replicating Directory Changes.*)"
| table _time, user, object_file_name, Object_Server, property
How it works:
-
EventCode=4662: Instructs Splunk to pull object access events from the security logs. -
Message=" *Replicating Directory Changes* ": Filters specifically for operations where directory replication data was requested. (Note: You can also search for the underlying GUID{1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}which corresponds to this property). -
rex field=...andtable ...: Extracts the exact property being accessed and formats it into a clean table alongside the user making the request and the target server. This allows you to quickly spot if a standard user (rather than a legitimate Domain Controller) is pulling replication data.
Detecting DCShadow (Rogue DC Registration):
index=main earliest=1690623888 latest=1690623890 EventCode=4742
| rex field=Message "(?P<gcspn>XX/[a-zA-Z0-9.-/]+)"
| table _time, ComputerName, Security_ID, Account_Name, user, gcspn
| search gcspn=*
How it works:
-
EventCode=4742: Tells Splunk to look for modifications to computer accounts within the domain. -
rex field=Message "(?P<gcspn>XX/[a-zA-Z0-9.-/]+)": Uses a regular expression to search the event message for the specific addition of a global catalog ServicePrincipalName (SPN), which is required for the attacker’s system to emulate a Domain Controller. -
search gcspn=*: The core trigger, it filters the results to only display instances where this specific SPN string was successfully identified and added to an account.
Zeek Network Hunts
The second half of the module shifts to Zeek-backed indexes. Same hunt pattern, but the signal is in network traffic instead of Windows Security or Sysmon.
11. Detecting RDP Brute Force
1. What is an RDP Brute Force Attack?
A Remote Desktop Protocol (RDP) brute force attack is a common method adversaries use to gain an initial foothold in a network. The concept is straightforward: attackers target an exposed RDP service and systematically guess different passwords until they successfully log into a remote session. This attack specifically exploits the fact that many organizations still rely on weak or default passwords.
2. How do you detect it?
Because brute-forcing requires a massive number of guesses to be successful, it generates a highly noisy footprint. You can detect this activity by monitoring your network traffic logs, specifically utilizing a tool like Zeek, to spot a high volume of RDP connection attempts originating from a single source IP address within a short timeframe.
3. The Splunk Query & Logic
(Note: As always, adjust your index or sourcetype names if they differ in your specific environment).
index="rdp_bruteforce" sourcetype="bro:rdp:json"
| bin _time span=5m
| stats count values(cookie) by _time, id.orig_h, id.resp_h
| where count>30
How it works:
-
sourcetype="bro:rdp:json": Instructs Splunk to pull Zeek network logs specifically parsed for RDP traffic. -
bin _time span=5m: Groups the logged network events into 5-minute intervals, which helps identify sudden, concentrated spikes in activity. -
stats count ... by id.orig_h, id.resp_h: Calculates the total number of connection attempts and groups them by where the attack is coming from (the source IP, orid.orig_h) and where it is aimed (the destination IP, orid.resp_h). -
where count>30: The core trigger, it alerts you if a single source IP launches more than 30 RDP connection attempts against a specific target within a 5-minute window.
12. Detecting Beaconing Malware
1. What is Cobalt Strike Beaconing?
Malware beaconing is a technique where an infected system periodically communicates back to an attacker’s Command and Control (C2) server, operating much like a lighthouse sending out a regular, repeating signal. Cobalt Strike is one of the most widely recognized and utilized C2 frameworks that relies on this behavior. Attackers use these beacons, typically small data packets sent over standard protocols like HTTP/HTTPS, DNS, or ICMP, to maintain a connection and receive instructions. The timing between these beacons can be completely fixed, or attackers can use “jitter” (slightly randomizing the timing) to make the automated traffic blend in with normal network behavior.
2. How do you detect it?
You detect beaconing by hunting for highly repetitive, predictable network communication patterns over time. Because attackers often use jitter, a simple search for an exact timestamp interval will fail. Instead, you must capture network traffic (often using a tool like Zeek) and use Splunk to mathematically calculate the average time difference between connections originating from the same source to the same destination. If a machine is making automated, periodic requests to an external server, you can expose the pattern by calculating the margins of that activity.
3. The Splunk Query & Logic
(Note: As always, adjust your index or sourcetype names if they differ in your specific environment).
index="cobaltstrike_beacon" sourcetype="bro:http:json"
| sort 0 _time
| streamstats current=f last(_time) as prevtime by src, dest, dest_port
| eval timedelta = _time - prevtime
| eventstats avg(timedelta) as avg, count as total by src, dest, dest_port
| eval upper=avg*1.1
| eval lower=avg*0.9
| where timedelta > lower AND timedelta < upper
| stats count, values(avg) as TimeInterval by src, dest, dest_port, total
| eval prcnt = (count/total)*100
| where prcnt > 90 AND total > 10
How it works:
-
sourcetype="bro:http:json": Instructs Splunk to pull Zeek network logs that are specifically formatted for HTTP traffic. -
streamstats ...&eval timedelta = _time - prevtime: Sorts the network events chronologically and calculates the exact time difference (timedelta) between a connection and the previous one for a specific source and destination IP. -
eval upper=avg *1.1&eval lower=avg* 0.9: This mathematically counters the attacker’s “jitter.” It calculates the average time between connections and builds a 10% upper and lower margin around that average. -
where prcnt > 90 AND total > 10: The core detection trigger, it alerts you if a source and destination IP have communicated more than 10 times, and over 90% of those connections occurred at the exact same average time interval (within that 10% margin).
13. Detecting Nmap Port Scanning
1. What is Nmap Port Scanning?
Port scanning with Nmap is a technique attackers and penetration testers use to probe networked systems for open ports, which act as ‘doors’ for data to pass in and out of a system. Nmap systematically attempts to establish a TCP handshake with various ports; if successful, the target might send back a “banner” revealing what service is running. A key characteristic of standard Nmap scanning is that it does not send any actual data payload, aside from the TCP handshake itself, the originating bytes sent are zero.
2. How do you detect it?
To detect Nmap scanning, you need to monitor your network traffic logs (such as Zeek logs) rather than host-level event logs. You can spot this activity by hunting for network connections where the originating payload is exactly zero bytes. By analyzing these specific zero-byte connections, you can look for a single source IP rapidly probing multiple distinct destination ports on your internal network within a short time window.
3. The Splunk Query & Logic
(Note: As always, adjust the index name if it differs in your environment. The source example happens to use an index named “cobaltstrike_beacon”, but it is specifically pulling Zeek connection logs for this search).
index="cobaltstrike_beacon" sourcetype="bro:conn:json" orig_bytes=0 dest_ip IN (192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8)
| bin span=5m _time
| stats dc(dest_port) as num_dest_port by _time, src_ip, dest_ip
| where num_dest_port >= 3
How it works:
-
sourcetype="bro:conn:json": Instructs Splunk to pull Zeek network connection logs. -
orig_bytes=0: The core filter, it isolates network events where no actual data payload was sent, which is highly indicative of a scanner like Nmap just checking if a port is open. -
dest_ip IN (...): Restricts the search to target internal/private IP address ranges (10.x.x.x, 172.16.x.x, 192.168.x.x). -
bin span=5m _time: Groups the events into 5-minute intervals to catch rapid, automated probing. -
stats dc(dest_port) ... by _time, src_ip, dest_ip: Calculates the distinct count (dc) of unique destination ports targeted by a specific source IP against a single destination IP. -
where num_dest_port >= 3: The trigger, it alerts you if a single source probes 3 or more distinct ports on a target within that 5-minute window.
In this lab: one source hit 2,000+ distinct ports against a single host in one 5-minute window.
14. Detecting Kerberos Brute Force Attacks
1. What is a Kerberos Brute Force/Enumeration Attack?
When adversaries perform Kerberos-based user enumeration or brute-forcing, they send an AS-REQ (Authentication Service Request) message containing a guessed username to the Key Distribution Center (KDC). They then analyze the server’s response to see if the account exists: a valid username typically returns a Ticket Granting Ticket (TGT) or a KRB5KDC_ERR_PREAUTH_REQUIRED error message. Conversely, an invalid username returns a specific KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN error in the AS-REP (Authentication Service Response) message. By rapidly cycling through usernames and monitoring these specific responses, attackers can quickly map out valid accounts on the target system.
2. How do you detect it?
Because this attack requires sending numerous requests to validate users, it leaves a distinct footprint in network traffic. You can detect this by monitoring Kerberos communications using a network tool like Zeek. You need to hunt for a high volume of failed Authentication Service (AS) requests originating from a single source IP address within a short timeframe, while specifically filtering out the standard “preauthentication required” errors to isolate the malicious probing.
3. The Splunk Query & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs).
index="kerberos_bruteforce" sourcetype="bro:kerberos:json" error_msg!=KDC_ERR_PREAUTH_REQUIRED success="false" request_type=AS
| bin _time span=5m
| stats count dc(client) as "Unique users" values(error_msg) as "Error messages" by _time, id.orig_h, id.resp_h
| where count>30
How it works:
-
sourcetype="bro:kerberos:json": Instructs Splunk to pull Zeek network logs specifically parsed for Kerberos protocol traffic. -
error_msg!=KDC_ERR_PREAUTH_REQUIRED success="false" request_type=AS: The core data filter, it isolates failed (success="false") Authentication Service (request_type=AS) requests, while explicitly ignoring the standardKDC_ERR_PREAUTH_REQUIREDmessages that legitimate valid users naturally trigger. -
bin _time span=5m: Groups the filtered network events into 5-minute intervals to catch rapid, automated probing. -
stats count dc(client) ... by _time, id.orig_h, id.resp_h: Aggregates the data by calculating the total number of failed requests (count) and the distinct number of unique usernames targeted (dc(client)), grouped by the time window, the attacker’s source IP (id.orig_h), and the KDC’s destination IP (id.resp_h). -
where count>30: The core detection trigger, it alerts you if a single source IP makes more than 30 of these anomalous failed requests against a target within that 5-minute window.
In this lab: one source tried 593 distinct usernames against the KDC in a single 5-minute window, all with the same unknown-principal error.
15. Detecting Kerberoasting (Network)
1. What is Kerberoasting?
We already covered detecting Kerberoasting through Windows Event Logs - hunting for RC4-encrypted service ticket requests (EventCode 4769) and using request volume to separate a real attack from normal Kerberos chatter. This section covers the same attack from the network side instead.
2. How do you detect it?
While you can hunt for Kerberoasting using Windows Event Logs (as covered in a previous section), you can also detect it purely through network traffic analysis. By utilizing a network monitoring tool like Zeek, you can analyze Kerberos packets (specifically TGS-REQ and TGS-REP packets) and hunt for service ticket requests that specifically request the weak RC4 cipher along with specific ticket flags.
3. The Splunk Query & Logic
(Note: Adjust the index and sourcetype names if your network logging setup differs).
index="sharphound" sourcetype="bro:kerberos:json" request_type=TGS cipher="rc4-hmac" forwardable="true" renewable="true"
| table _time, id.orig_h, id.resp_h, request_type, cipher, forwardable, renewable, client, service
How it works:
-
sourcetype="bro:kerberos:json": Instructs Splunk to pull Zeek network logs specifically parsed for Kerberos protocol traffic. -
request_type=TGS: Filters the logs to only look at Ticket Granting Service (TGS) requests. -
cipher="rc4-hmac": The core detection trigger, it isolates ticket requests that are explicitly utilizing weak RC4 encryption (ARCFour-HMAC-MD5), which attackers rely on for offline cracking. -
forwardable="true" renewable="true": Further filters the results for specific ticket options that are commonly requested by automated Kerberoasting tools.
In this lab: one account requested the same service 14 times within about one second under the RC4/forwardable/renewable filter.
16. Detecting Golden Tickets
1. What is a Golden Ticket Attack (Network Perspective)?
In a typical Kerberos authentication flow, a client must first request a Ticket Granting Ticket (TGT) from the Domain Controller, known as the AS-REQ and AS-REP process, before they can use that TGT to request a Service Ticket (TGS) to access a specific resource.
In a Golden Ticket (or Pass-the-Ticket) attack, the attacker completely bypasses this initial authentication process. Because they have generated a forged TGT (or stolen an existing one), they can jump straight to directly requesting TGS tickets to access services on the network without ever performing the standard AS-REQ and AS-REP steps.
2. How do you detect it?
While network monitoring tools like Zeek lack the ability to natively verify if a ticket is mathematically forged, you can detect the attack by hunting for the anomalous network behavior it creates. Specifically, you want to monitor your network traffic to find instances of “orphaned” Service Ticket requests, meaning a client machine is making TGS requests (asking for service access) without any preceding AS-REQ requests (asking for initial authentication) occurring within the same timeframe.
3. The Splunk Query & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs. This example relies on Zeek logs).
index="golden_ticket_attack" sourcetype="bro:kerberos:json"
| where client!="-"
| bin _time span=1m
| stats values(client), values(request_type) as request_types, dc(request_type) as unique_request_types by _time, id.orig_h, id.resp_h
| where request_types=="TGS" AND unique_request_types==1
How it works:
-
sourcetype="bro:kerberos:json": Instructs Splunk to pull Zeek network logs specifically parsed for Kerberos protocol traffic. -
where client!="-": Cleans up the data by filtering out noisy network events where the client information is missing or unavailable. -
bin _time span=1m: Divides the network data into 1-minute intervals to analyze patterns of authentication requests over a short, concentrated period. -
stats ... dc(request_type) ... by _time, id.orig_h, id.resp_h: Aggregates the events based on the source IP (id.orig_h) and destination IP (id.resp_h). It pulls all the different Kerberos request types made during that 1-minute window and counts exactly how many distinct types occurred. -
where request_types=="TGS" AND unique_request_types==1: The core detection trigger, it isolates instances where the only Kerberos request made by the client during that minute was a TGS request. This alerts you to an incomplete authentication flow, strongly indicating an attacker is using a forged Golden Ticket or stolen session.
In this lab: one client only showed TGS requests with no AS-REQ in the dataset.
17. Detecting Cobalt Strike PsExec
1. What is Cobalt Strike PSExec Lateral Movement?
Cobalt Strike’s PSExec is a lateral movement technique that mimics Microsoft’s legitimate Sysinternals PsExec tool to execute post-exploitation payloads on remote systems. Operating over the SMB protocol (port 445) and requiring local administrator privileges, the tool works in a specific sequence: it transfers a payload over the network to a hidden administrative share (like the ADMIN$ share), creates a temporary remote service to execute that payload, and then immediately stops and deletes the service to minimize its forensic footprint.
2. How do you detect it?
Because the attacker deletes the service after execution to hide their tracks, one of the most reliable ways to detect this activity is by monitoring the network for the initial payload delivery. Utilizing a network monitoring tool like Zeek, you can inspect SMB protocol traffic and hunt for the specific file transfer phase. You are looking for instances where executable files are being opened or written directly to administrative network shares.
3. The Splunk Query & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs).
index="cobalt_strike_psexec" sourcetype="bro:smb_files:json" action="SMB::FILE_OPEN" name IN ("*.exe", "*.dll", "*.bat") path IN ("*\\C$", "*\\ADMIN$") size>0
How it works:
-
sourcetype="bro:smb_files:json": Instructs Splunk to pull Zeek network logs specifically parsed for SMB file transfer activity. -
action="SMB::FILE_OPEN": Filters the logs to find instances where a file is actively being opened or written over the network connection. -
name IN ("*.exe", "*.dll", "*.bat"): Narrows the search to specifically look for executable files, DLLs, or batch scripts, which are the standard formats for these malicious payloads. -
path IN ("*\\C$", "*\\ADMIN$"): The core detection trigger, it isolates file transfers aimed specifically at hidden administrative shares (C$orADMIN$), which is exactly where Cobalt Strike defaults to dropping its PSExec payloads. -
size>0: Ensures that an actual file with data was transferred, filtering out empty or failed requests to reduce noise.
In this lab: one executable transfer to the DC ADMIN$ share (be5312f.exe).
18. Detecting Zerologon
1. What is the Zerologon Vulnerability?
Zerologon (CVE-2020-1472) is a critical authentication bypass vulnerability in Microsoft’s Netlogon Remote Protocol (MS-NRPC). The flaw exists because the initialization vector (IV) for the protocol’s AES-CFB8 encryption mode is mistakenly hardcoded to a fixed value of all zeros, rather than being uniquely randomized. Attackers exploit this by sending a session key consisting entirely of zeros, which successfully bypasses the authentication process. Once authenticated, the attacker can execute the NetrServerPasswordSet2 function to change the Domain Controller’s computer account password to a blank value, instantly granting them full control over the entire Active Directory domain.
2. How do you detect it?
Exploiting Zerologon requires the attacker to spam the server with specific Netlogon messages until the zero-key authentication succeeds, a process that usually executes within seconds. You can detect this from a network perspective by monitoring for a massive spike in these specific Netlogon requests. Utilizing a network monitoring tool like Zeek, you can hunt for a rapid sequence of NetrServerReqChallenge, NetrServerAuthenticate3, and NetrServerPasswordSet2 operations occurring between a single client and the Domain Controller.
3. The Splunk Query & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs).
index="zerologon" endpoint="netlogon" sourcetype="bro:dce_rpc:json"
| bin _time span=1m
| where operation == "NetrServerReqChallenge" OR operation == "NetrServerAuthenticate3" OR operation == "NetrServerPasswordSet2"
| stats count values(operation) as operation_values dc(operation) as unique_operations by _time, id.orig_h, id.resp_h
| where unique_operations >= 2 AND count>100
How it works:
-
sourcetype="bro:dce_rpc:json" endpoint="netlogon": Instructs Splunk to pull Zeek network logs specifically parsed for DCE-RPC protocol traffic hitting the Netlogon endpoint. -
bin _time span=1m: Groups the network events into 1-minute intervals to catch the sudden burst of requests required to exploit the cryptographic flaw. -
where operation == ...: Filters the logs for the three specific Netlogon functions utilized during the attack: requesting the challenge, attempting the authentication, and setting the password. -
stats ... dc(operation) ... by _time, id.orig_h, id.resp_h: Aggregates the events based on the source IP (id.orig_h) and destination IP (id.resp_h), while calculating the distinct count (dc) of unique operation types seen. -
where unique_operations >= 2 AND count>100: The core detection trigger, it flags an alert if a single source IP fires off more than 100 of these specific Netlogon operations, encompassing at least two different function types, within a single 60-second window.
In this lab: one source sent 881 combined NetrServerReqChallenge/NetrServerAuthenticate3 calls against the DC across two 1-minute windows.
19. Detecting HTTP Exfiltration
1. What is HTTP POST Data Exfiltration?
Data exfiltration inside an HTTP POST body is a technique attackers use to stealthily extract sensitive information from a compromised system. By packing the stolen data into the body of an HTTP POST request and sending it to their Command and Control (C2) server, attackers disguise the exfiltration as normal web traffic, since POST requests are constantly used for legitimate activities like form submissions and file uploads. To blend in even further, attackers often utilize innocuous URLs and standard web headers to camouflage the outbound data.
2. How do you detect it?
Because POST requests are a standard part of daily web traffic, you cannot simply flag every instance. Instead, you detect this by employing network monitoring tools (like Zeek) to aggregate all the data being sent to specific external IPs and ports. By analyzing the total volume of outgoing traffic, you can identify patterns of unusually large or abnormally frequent data transfers destined for a single, external location.
3. The Splunk Query & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs).
index="cobaltstrike_exfiltration_http" sourcetype="bro:http:json" method=POST
| stats sum(request_body_len) as TotalBytes by src, dest, dest_port
| eval TotalBytes = TotalBytes/1024/1024
How it works:
-
sourcetype="bro:http:json": Instructs Splunk to pull Zeek network logs specifically parsed for HTTP traffic. -
method=POST: Filters the logs to exclusively look at HTTP POST requests, stripping away standard web browsing (GET requests). -
stats sum(request_body_len) as TotalBytes by src, dest, dest_port: The core aggregation logic. It calculates the total sum of the data payload (request_body_len) sent out, grouped by the source IP, destination IP, and destination port. -
eval TotalBytes = TotalBytes/1024/1024: A helpful mathematical function that converts the massive raw byte count into Megabytes, making it much easier for analysts to read the final table and spot unusually large outbound transfers.
In this lab: 256 MB of HTTP POST data from one internal host to one external IP on port 80.
20. Detecting DNS Exfiltration
1. What is DNS Exfiltration?
DNS-based exfiltration is a stealthy technique attackers use to covertly steal data, exploiting the fact that DNS traffic is almost always allowed by default through network firewalls. Once an attacker compromises a network and identifies sensitive data, they encode or encrypt it, split it into small chunks, and embed those chunks directly into the subdomains of DNS queries. These queries are sent to a DNS server controlled by the attacker, who then extracts, reassembles, and decodes the stolen data.
2. How do you detect it?
Because attackers have to cram stolen data into subdomain fields, exfiltration generates unusually long DNS queries. You can detect this by utilizing network monitoring tools like Zeek to analyze DNS traffic. The detection strategy relies on calculating the character length of all DNS queries, filtering out known legitimate services that naturally produce long strings, and alerting when a single host sends an abnormally high volume of these lengthy queries over a specific timeframe.
3. The Splunk Query & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs).
index=dns_exf sourcetype="bro:dns:json"
| eval len_query=len(query)
| search len_query>=40 AND query!="*.ip6.arpa*" AND query!="*amazonaws.com*" AND query!="*._googlecast.*" AND query!="*_ldap.*"
| bin _time span=24h
| stats count(query) as req_by_day by _time, id.orig_h, id.resp_h
| where req_by_day>60
| table _time, id.orig_h, id.resp_h, req_by_day
How it works:
-
sourcetype="bro:dns:json": Instructs Splunk to pull Zeek network logs specifically parsed for DNS protocol traffic. -
eval len_query=len(query): A mathematical function that calculates the exact character length of every logged DNS query. -
search len_query>=40 AND query!="*...": The core data filter. It isolates queries that are unusually long (40 characters or more), while specifically excluding known benign domains (like AWS, Google Cast, standard LDAP, or IPv6 reverse lookups) that naturally generate long DNS strings. -
bin _time span=24h: Groups the filtered network events into 24-hour intervals to catch slow, persistent data exfiltration attempts over a full day. -
stats count(query) ... by _time, id.orig_h, id.resp_h: Aggregates the data by calculating the total number of these abnormally long queries, grouped by the time window, the source IP (id.orig_h), and the destination IP (id.resp_h). -
where req_by_day>60: The core detection trigger, it alerts you if a single source sends more than 60 of these suspicious, lengthy DNS queries to a destination within that 24-hour window.
In this lab: 17,116 long DNS queries in one day from one host to one destination, 13,402 distinct (~78%).
21. Detecting Ransomware
1. What is Ransomware (SMB Execution)?
When ransomware infects a machine, it often traverses the network using the Server Message Block (SMB) protocol to find and encrypt files stored on shared drives. Attackers typically use one of two distinct approaches over SMB to lock down these files:
-
The File Overwrite Approach: The ransomware opens the file, encrypts the data directly within the system’s memory, and then overwrites the original file on the network share with the encrypted version. This method leaves fewer forensic traces.
-
The File Renaming Approach: The ransomware reads the file, encrypts it, and then renames the file by appending a unique extension (e.g.,
.lock,.crypt, or a random string) indicating it has been taken hostage.
2. How do you detect it?
Because these operations are happening over a network share, you can detect them by monitoring SMB file operations using a network monitoring tool like Zeek.
-
For the Overwrite Approach: You must monitor for an excessive, rapid combination of file open (
SMB::FILE_OPEN) and file rename/overwrite (SMB::FILE_RENAME) operations originating from a single source. -
For the Renaming Approach: You must hunt for a massive spike in files being renamed where the new file extension completely differs from the original, and specifically where a high volume of files are suddenly sharing the exact same new extension.
3. The Splunk Queries & Logic
(Note: As always, adjust the index and sourcetype names if your network logging setup differs).
Detecting the Overwrite Approach:
index="ransomware_open_rename_sodinokibi" sourcetype="bro:smb_files:json"
| where action IN ("SMB::FILE_OPEN", "SMB::FILE_RENAME")
| bin _time span=5m
| stats count by _time, source, action
| where count>30
| stats sum(count) as count values(action) dc(action) as uniq_actions by _time, source
| where uniq_actions==2 AND count>100
How it works:
-
sourcetype="bro:smb_files:json": Instructs Splunk to pull Zeek network logs specifically parsed for SMB file actions. -
where action IN ("SMB::FILE_OPEN", "SMB::FILE_RENAME"): Isolates the two specific SMB actions ransomware utilizes during the overwrite process. -
bin _time span=5m: Groups the events into 5-minute intervals to catch rapid, automated encryption loops. -
where uniq_actions==2 AND count>100: The core detection trigger, it flags an alert if a single source IP executes over 100 total actions within a 5-minute window, and ensures bothFILE_OPENandFILE_RENAMEactions are actively occurring.
Detecting the Renaming Approach (Anomalous Extensions):
index="ransomware_new_file_extension_ctbl_ocker" sourcetype="bro:smb_files:json" action="SMB::FILE_RENAME"
| bin _time span=5m
| rex field="name" "\.(?<new_file_name_extension>[^\.]*)$"
| rex field="prev_name" "\.(?<old_file_name_extension>[^\.]*)$"
| stats count by _time, id.orig_h, id.resp_p, name, source, old_file_name_extension, new_file_name_extension
| where new_file_name_extension!=old_file_name_extension
| stats count by _time, id.orig_h, id.resp_p, source, new_file_name_extension
| where count>20
| sort -count
How it works:
-
action="SMB::FILE_RENAME": Narrows the focus exclusively to files being renamed over SMB. -
rex field="name"...andrex field="prev_name"...: These two regular expressions automatically extract the file extension from both the old file name and the new file name so they can be compared. -
where new_file_name_extension!=old_file_name_extension: Filters the noise by only showing instances where the actual file extension was changed (ignoring standard renames where just the file’s title was altered). -
stats count ... by new_file_name_extension | where count>20: The core detection trigger, it groups the remaining events by the new extension and alerts you if more than 20 files are assigned the exact same new extension within a 5-minute window.
Overwrite approach: looks for high SMB::FILE_OPEN + SMB::FILE_RENAME volume from one host in a short window.
Renaming approach: looks for many files renamed to the same new extension in a short window.
In this lab: the overwrite query returned 22,073 combined open/rename actions from one source in one 5-minute window; the renaming query returned 4,227 files all renamed to .zhqxelf by one source against one host in the same window.
Skill Assessment
The skill assessment walkthrough is longer and more step-by-step than the main hunt sections, so I kept it on a separate page: Module 6: Skill Assessment (Empire beaconing TimeInterval, PrintNightmare source IP, and BloodHound collector IP).
Key Takeaway
Splunk hunting here came down to picking the right Zeek log for the attack, then asking one of four math questions instead of looking for a tool name.
What stuck with me:
- Every detection was really just one of four shapes: Spike (too fast), Breadth (too many targets), Broken Chain (a step missing), or Heavy Package (too much data). Identify which shape the attack takes before writing the query, not after.
- Field names lie by default in Zeek logs (
source,cookie,error_msgall meant something other than the obvious guess). Check one raw event before trusting any borrowed query. - The same attack can show more than one shape (Kerberoasting was breadth or burst). Check both before deciding which one actually fired.
- Baseline math (
avg/stdev) needs a second population to compare against. In a small lab with one attacker/one victim it goes quiet instead of failing loud, so fall back to absolute volume when that happens. - A hit is a starting point, not a verdict. Confirm the attempt vs. the compromise, the pattern vs. the intent, with a second corroborating signal before calling anything confirmed.
References
[HTB Academy] Certified Defensive Security Analyst (CDSA) - Module 6: Detecting Windows Attacks with Splunk. https://academy.hackthebox.com/
[SPL Threat-Hunting Library] My reusable, environment-agnostic Splunk query library, organized by attacker phase - this is where these queries (and others from the series) live in cleaned-up, placeholder-swappable form. https://github.com/kismatkunwar89/spl-threat-hunting-library