OSTH Exam - Guided By RedBlock
TH-200: Foundational Threat Hunting - OffSec Threat Hunter

OSTH Field Guide (Complete Edition) — Foundational Threat Hunting (OffSec TH-200)
The large, end-to-end defensive reference for OSTH / TH-200: hunting methodology and frameworks → data sources and telemetry engineering → Splunk SPL + KQL + Elastic → per-tactic hunting mapped to MITRE ATT&CK → AD-attack detection → network hunting → four worked intrusion reconstructions → detection engineering → a hunt-hypothesis library. Every query finds adversary activity; this is detection content throughout.
Exam reality: OSTH is a blue-team practical. You're handed telemetry (Splunk / logs) and must reconstruct a threat actor's activity and answer exact questions — a SHA256, a created username, a dump filename, a timestamp, an exfil IP, the spread path. You win by pivoting from one lead through the whole kill-chain and reading the story out of the logs. The method never changes: broad → narrow → confirm → pivot → document. This guide is defensive. It teaches you to see attacks in data, not to run them — the offensive TTPs are named only so you know their telemetry.
Table of Contents
Threat-Hunting Methodology & Frameworks
The Exam Workflow & Answer-Submission Helper
Data Sources, Telemetry & Coverage
Windows Event Logs & Logon Types
Sysmon: Events & Configuration
Splunk SPL for Hunting
KQL (Defender / Sentinel) for Hunting
Query Translation Cheat — SPL ↔ KQL ↔ Elastic
MITRE ATT&CK as a Hunt Map
LOLBins & Suspicious Binary Reference
Hunting: Initial Access & Execution
Hunting: Persistence
Hunting: Privilege Escalation & Defense Evasion
Hunting: Credential Access
Hunting: Lateral Movement
Hunting: Discovery & Collection
Hunting: Command & Control + Exfiltration
Hunting Active Directory Attacks (detection counterpart)
IOCs, Threat Intelligence & the Pyramid of Pain
ELK / Elastic & Zeek Network Hunting
Baselining, Correlation & Timeline Building
Worked Hunt #1 — File IOC → Full Kill-Chain
Worked Hunt #2 — Logon-Spray Spike
Worked Hunt #3 — Beaconing / C2 Lead
Worked Hunt #4 — Data Staging & Exfiltration
Detection Engineering — Saved Searches, Rules, Sigma
Hunt-Hypothesis Library
False Positives & Tuning
Linux & macOS Threat Hunting
Cloud & Identity Hunting (Entra / M365 / AWS)
Live Response & Triage
Reporting, Metrics & Glossary
1. Threat-Hunting Methodology & Frameworks
What hunting is. Threat hunting is the proactive, hypothesis-led search through telemetry for adversary activity that automated alerts missed. Alerts are reactive (they fire on known-bad); hunting assumes a breach may already be present and goes looking. The output of a hunt is not just "found/not found" — it's new detections, refined baselines, and documented coverage gaps.
Three ways a hunt starts:
Hypothesis-driven — "If an adversary performed technique X, then telemetry Y would exist." Grounded in MITRE ATT&CK. Most durable. E.g. "If they dumped LSASS, a non-system process opened a handle to
lsass.exe(Sysmon EID 10)."IOC / intel-driven — you're handed an indicator (hash, IP, domain, filename) and pivot outward to see if and how it touched the environment.
Anomaly / baseline-driven — you stack-count a data source and investigate the long tail (rare processes, rare parent/child pairs, rare destinations).
The hunt loop (memorize — it's the whole exam):
1. LEAD hypothesis, IOC, or anomaly
2. BROAD wide query across the right source + time window
3. NARROW filter to suspicious host/user/process; subtract known-good
4. CONFIRM inspect the exact events (command line, parent, hashes, user)
5. PIVOT follow the actor: same host/user, adjacent time, child procs, network
6. DOCUMENT IOCs, affected assets, ATT&CK-mapped timeline, new detections
Pivot anchors. Every confirmed event hands you fields you pivot on: host, account/SID, process (PID + Sysmon ProcessGuid), parent process, timestamp, file hash, destination IP/domain. A hunt is a chain of pivots across these anchors — each stage of the kill-chain shares an anchor with the next.
Frameworks worth naming (for the report):
PEAK (Prepare, Execute, Act with Knowledge) — modern hunt framework: prepare (hypothesis + data check) → execute (hunt) → act (document detections/gaps).
TaHiTI (Targeted Hunting integrating Threat Intelligence) — intel → abstract → hunt.
Sqrrl / Cyber Kill Chain / MITRE ATT&CK — the mental models for adversary progression.
Hunting Maturity Model (HMM0–HMM4) — from no hunting (relying on alerts) to automated, data-driven hunting feeding detection engineering.
Mindset. Assume compromise. Prefer behavior over signatures (a renamed mimikatz.exe still opens an LSASS handle). Timebox. Cast broad, then cut hard. And always ask, at the end: "what saved search would have caught this at hour zero?" — that question is the point of hunting.
2. The Exam Workflow & Answer-Submission Helper
OSTH questions demand an exact value — a SHA256, a created username, a dump filename, a timestamp. The efficient loop: hunt to a small candidate set → dump candidates → bulk-check.
Workflow:
Hunt (SPL/KQL) to produce candidate values — e.g., every hash of a file dropped in a suspicious path, or every username created in the window.
Export candidates to
in.txt; put the provided answer forms inanswer.txt.Run the checker; the match is your answer.
run.ps1 (bulk answer checker against flags.exe):
# transfer run.ps1 to the folder with flags.exe (Desktop by default)
# answer.txt = provided answer hashes ; in.txt = your candidate inputs
# powershell: Set-ExecutionPolicy Bypass ; .\run.ps1
Get-Content "in.txt" | ForEach-Object {
$output = ./flags.exe $_
$in = $_
$matchFound = $false
Get-Content "answer.txt" | ForEach-Object {
if ($output -eq $_) {
Write-Host "[*] Match found for flag: $_ on input $in"
$matchFound = $true
break # stop on first match; comment out to check all
}
}
if (-not $matchFound) { Write-Host "No match for: $output : $_" }
}
The script only automates comparison — the hunting that produces good candidates is the real work. Keep in.txt tight (a dozen candidates, not thousands) by narrowing your query well before you dump.
Answer-type tip: when a question is ambiguous ("what did the actor do?"), enumerate every plausible answer form — new username, new password, group name, task name, filename, hash — into in.txt and let the checker disambiguate. That's exactly how a single mimikatz-plus-net user command block resolves to "the new username."
3. Data Sources, Telemetry & Coverage
Hunting is only as good as the telemetry. Know what each source gives and where your blind spots are.
Source | Key data | Hunts it powers |
|---|---|---|
Windows Security | logons 4624/4625, priv use 4672, account/group 4720/4728/4732, process 4688 | auth, account manipulation, execution |
Sysmon | process(1), net(3), imageload(7), remotethread(8), procaccess(10), file(11), registry(12-14), WMI(19-21), DNS(22) | the richest endpoint hunting |
PowerShell | ScriptBlock 4104, Module 4103 | decoded script content, obfuscation |
EDR / Defender |
| cross-host behavioral hunting |
AD / Kerberos | 4768/4769/4776, DS access 4662, replication | Kerberoast, DCSync, golden ticket |
Network | firewall, proxy, DNS, Zeek, NetFlow, Suricata | C2, exfil, lateral movement |
App / Cloud | IIS/web, VPN, O365/Azure sign-in, cloud audit | web attacks, identity, cloud |
CIM / normalization. In Splunk, the Common Information Model maps disparate sources into shared field names (e.g., Processes, Authentication, Network_Traffic datamodels) so one query spans sources. Learn the datamodel fields (Processes.process_name, Authentication.user, Network_Traffic.dest_ip) — many hunts use | tstats over accelerated datamodels for speed:
| tstats count from datamodel=Endpoint.Processes where Processes.process_name="powershell.exe" by Processes.dest, Processes.user
Coverage mapping. Before hunting a technique, confirm you have the data for it. LSASS-dump hunting needs Sysmon EID 10 (or an EDR); Kerberoast needs 4769 with encryption type; command-line hunting needs 4688-with-cmdline or Sysmon EID 1. Map your sources to ATT&CK (DeTT&CT / ATT&CK Navigator) so you know which techniques you're blind to — an unhuntable technique is a finding in itself.
Time discipline. Normalize to one timezone (UTC ideally). Watch for log-source clock skew when correlating across hosts — a few minutes of drift can scramble a timeline; anchor on a single reliable source where possible.
4. Windows Event Logs & Logon Types
Security event IDs to know cold:
4624 logon success 4625 logon failure 4634/4647 logoff
4672 special privileges assigned (admin-equiv logon)
4688 process creation (ENABLE command-line auditing)
4689 process exit
4720 user created 4722 enabled 4725 disabled 4726 deleted 4738 changed
4728 member added to global group 4732 local group 4756 universal group
4740 account locked out 4767 account unlocked
4698 scheduled task created 4699 deleted 4702 updated
4697 service installed (Security) 7045 service installed (System)
1102 security log cleared (!) 104 event log cleared (System)
4104 PowerShell ScriptBlock 4103 Module logging 400/600 engine
4768 Kerberos TGT requested 4769 Kerberos TGS requested 4771 pre-auth failed
4776 NTLM auth 4662 operation on an AD object (DCSync hunting)
5140/5145 network share access 5156 WFP connection allowed
Logon types (the meaning of the number in 4624/4625) — critical for lateral-movement hunting:
2 Interactive (console) 10 RemoteInteractive (RDP)
3 Network (SMB, shares, PtH) 11 CachedInteractive
4 Batch (scheduled task) 5 Service
7 Unlock 9 NewCredentials (runas /netonly — PtH/OPtH tell)
8 NetworkCleartext (IIS basic, etc.)
Type 3 = remote access to resources (SMB, WMI, PsExec-ish); type 10 = RDP; type 9 = alternate credentials injected into a session (a classic pass-the-hash / overpass indicator). 4672 right after a 4624 = a high-privilege logon worth scrutinizing.
Enable the right auditing (or you're blind): command-line process auditing (so 4688 carries arguments), PowerShell ScriptBlock logging (4104), Kerberos service-ticket logging with encryption type (4769), and object-access auditing on the domain (4662 for DCSync). If Sysmon is deployed, EID 1 gives you command line + hashes + parent regardless.
5. Sysmon: Events & Configuration
Sysmon is the highest-value endpoint source — if it's configured and forwarded.
Sysmon event IDs:
1 ProcessCreate Image, CommandLine, ParentImage, Hashes, User, ProcessGuid (core)
2 FileCreateTime timestomping
3 NetworkConnect egress / C2 (scope to reduce noise)
5 ProcessTerminate
7 ImageLoad unsigned/rare DLL loads (injection, DLL search-order)
8 CreateRemoteThread classic injection
9 RawAccessRead raw disk (SAM theft, anti-forensics)
10 ProcessAccess handle to lsass.exe = credential-dump tell (scope to lsass)
11 FileCreate drops in Temp/Tasks/Startup/Public
12/13/14 Registry Run keys, service installs, persistence (13 = value set)
15 FileCreateStreamHash Alternate Data Streams / MOTW
17/18 Pipe named-pipe C2 (Cobalt Strike default pipes)
19/20/21 WMI event-subscription persistence
22 DnsQuery DGA / DNS exfil
23 FileDelete defense evasion
25 ProcessTampering process hollowing / herpaderping
Configuration principles. Start from a community baseline — SwiftOnSecurity's sysmon-config or Olaf Hartong's sysmon-modular — then tune: include high-value events, exclude verified known-good (signed OS binaries, your EDR/backup agents) so signal isn't buried. Always log command line and SHA256 hashes. Scope noisy events (EID 3 network, EID 7 image load) to processes/paths of interest rather than everything.
sysmon.exe -accepteula -i sysmonconfig.xml # install with config
sysmon.exe -c sysmonconfig.xml # update config
sysmon.exe -c # dump current config
Onboarding. Forward Microsoft-Windows-Sysmon/Operational via Splunk Universal Forwarder, Winlogbeat/Elastic Agent → ELK, or Azure Monitor Agent → Sentinel. Verify field extraction (Image, CommandLine, Hashes, ParentImage, ProcessGuid) resolves so your queries work. Pair with Windows Event Forwarding (WEF) to a collector for hosts without an agent. Know your gaps — an event you don't collect is an attack you can't hunt.
6. Splunk SPL for Hunting
Core verbs:
search / where filter stats / eventstats / streamstats aggregate
table / fields project columns dedup / uniq distinct
sort / head / tail order/limit rare / top freq analysis
rex / eval extract/compute lookup / inputlookup enrich
transaction group into sessions bin / bucket time windows
tstats fast datamodel query timechart time series
Patterns you'll reuse constantly:
# process hunting baseline
index=* sourcetype=Sysmon EventCode=1
| table _time host User Image CommandLine ParentImage Hashes
# frequency / long-tail (rare = suspicious)
index=* sourcetype=Sysmon EventCode=1 | stats count by Image | sort count
# per-host session grouping
index=* host=WKS01 | transaction host maxpause=5m | table _time duration eventcount
# unique IOCs for bulk-checking
index=* Hashes=* Image="*\\Downloads\\*" | rex field=Hashes "SHA256=(?<sha>[0-9A-Fa-f]{64})" | dedup sha | table sha
# readable timeline
... | eval t=strftime(_time,"%F %T") | sort _time | table t host User CommandLine
# activity window per host
index=* | stats earliest(_time) as first latest(_time) as last by host
| eval first=strftime(first,"%F %T"), last=strftime(last,"%F %T")
# fast datamodel search
| tstats count from datamodel=Endpoint.Processes where Processes.process_name="cmd.exe" by Processes.dest Processes.parent_process_name
Your IOC-hash hunt (folded in):
index=* Hashes=* (securitytools OR security_update OR "C:\\Windows\\Tasks\\*"
OR "C:\\Users\\h.jones\\Downloads\\*" OR mimikatz.exe OR nxc.exe OR winPEASany.exe
OR db_exfil.exe OR tickets.exe OR test.log OR dbstatus.exe OR creds.exe OR *.bat OR securitytools.zip)
| table Hashes | uniq
Dump Hashes → in.txt, provided answers → answer.txt, run run.ps1. Pivot tips: eventstats adds an aggregate without collapsing rows (great for "count per host, keep detail"); streamstats computes running deltas (beacon interval analysis); lookup enriches with an IOC list you inputlookup.
7. KQL (Defender / Sentinel) for Hunting
(Kusto — Microsoft Defender / Sentinel. Note: index=* above is Splunk SPL, not KQL — KQL is the Defender/Sentinel dialect.)
// suspicious process command lines
DeviceProcessEvents
| where Timestamp > ago(7d)
| where ProcessCommandLine has_any ("mimikatz","sekurlsa","-enc","FromBase64","Invoke-","lsass",".dmp")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, SHA256
// dropped files in suspicious paths (unique hashes)
DeviceFileEvents
| where FolderPath has @"C:\Windows\Tasks\" or FolderPath has @"\Downloads\" or FolderPath has @"\Public\"
| distinct SHA256, FileName, FolderPath, InitiatingProcessAccountName
// LSASS access (credential dumping)
DeviceEvents
| where ActionType == "OpenProcessApiCall" and FileName == "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsMpEng.exe","wininit.exe","csrss.exe")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine
// remote logons for lateral movement
DeviceLogonEvents
| where LogonType in ("Network","RemoteInteractive")
| summarize count(), make_set(DeviceName) by AccountName, RemoteIP, LogonType
// parent/child anomaly (Office spawning a shell)
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","outlook.exe")
| where FileName in~ ("powershell.exe","cmd.exe","mshta.exe","wscript.exe")
Operators to live in: where, project, extend, summarize ... by, distinct, count()/dcount()/make_set()/make_list(), has/has_any/contains/in~, join kind=inner/leftouter, parse, ago(), bin(Timestamp,1h), top N by, arg_max(). Correlate stages with join: logon → process → network on DeviceName + time window.
8. Query Translation Cheat — SPL ↔ KQL ↔ Elastic
The same hunt, three dialects — you'll switch between them.
Intent | Splunk SPL | KQL (Kusto) | Elastic (KQL/Lucene) |
|---|---|---|---|
filter |
|
|
|
select cols |
|
| (columns in Discover) |
aggregate |
|
| aggregations / |
distinct |
|
|
|
top/rare |
|
|
|
contains |
|
|
|
time window |
|
|
|
regex extract |
|
| grok/dissect (ingest) |
sort |
|
| sort on field |
join |
|
|
|
sequence |
| (manual join) | EQL |
EQL (Elastic Query Language) uniquely does ordered sequences natively — great for "parent spawns shell, then shell makes a network connection within 30s":
sequence by host.id with maxspan=30s
[process where process.parent.name=="winword.exe" and process.name=="powershell.exe"]
[network where destination.port in (80,443)]
9. MITRE ATT&CK as a Hunt Map
ATT&CK gives the tactic order to walk when reconstructing an intrusion, and the technique IDs to map findings to (the report is graded on this).
Reconnaissance -> Resource Dev -> Initial Access -> Execution -> Persistence
-> Privilege Escalation -> Defense Evasion -> Credential Access -> Discovery
-> Lateral Movement -> Collection -> Command & Control -> Exfiltration -> Impact
High-yield techniques to hunt (and their telemetry):
T1566 Phishing Office->shell parent/child, mail gateway
T1059.001 PowerShell 4104 ScriptBlock, -enc, download cradles
T1059.003 cmd / .005 VBS 4688/Sysmon1 command lines
T1053.005 Scheduled Task 4698 / schtasks /create
T1543.003 Service 7045 / 4697
T1547.001 Run key Sysmon 13 CurrentVersion\Run
T1003.001 LSASS dump Sysmon 10 handle to lsass; comsvcs MiniDump
T1003.002/.003 SAM/NTDS reg save, shadowcopy, ntdsutil
T1558.003 Kerberoast 4769 RC4 bursts
T1550.002 Pass-the-Hash 4624 type 9, NTLM
T1021.001 RDP / .002 SMB 4624 type 10 / type 3, PSEXESVC
T1071 C2 / T1041 Exfil Sysmon 3/22, proxy bytes_out, beacon intervals
T1070.001 Clear logs 1102 / 104
Map every confirmed event to an ID; §11–18 are organized by tactic.
10. LOLBins & Suspicious Binary Reference
Adversaries "live off the land" using signed, built-in binaries (LOLBins) to blend in. Hunt these by command-line context, not just presence (many are used legitimately).
certutil.exe -urlcache/-decode download/decode payloads
bitsadmin.exe /transfer download
mshta.exe http(s)/vbscript run remote HTA/script
regsvr32.exe /i:http scrobj.dll "squiblydoo" remote scriptlet
rundll32.exe javascript:/ *.dll,fn proxy execution
wmic.exe process call create remote/lateral exec
powershell.exe -enc/-e/IEX/DownloadString staging
msbuild.exe / installutil.exe / regasm .NET inline/bypass execution
mavinject.exe / dllhost.exe injection
esentutl.exe / makecab / expand copy/stage
wscript/cscript scripts
comsvcs.dll (rundll32 ... MiniDump) LSASS dump
# hunt LOLBin command-line context (not mere presence)
index=* sourcetype=Sysmon EventCode=1 (
(Image="*\\certutil.exe" (CommandLine="*urlcache*" OR CommandLine="*-decode*"))
OR (Image="*\\regsvr32.exe" CommandLine="*scrobj*")
OR (Image="*\\rundll32.exe" CommandLine="*javascript:*")
OR (Image="*\\mshta.exe" (CommandLine="*http*" OR CommandLine="*vbscript*"))
OR (Image="*\\wmic.exe" CommandLine="*process call create*")
) | table _time host User Image CommandLine ParentImage
Reference lists: LOLBAS (Windows), GTFOBins (Linux). Tells: a LOLBin with a URL, a \Temp\/\Public\ path, base64, or an unusual parent (Office, services.exe, a browser).
11. Hunting: Initial Access & Execution
Malicious parent/child (macro / LOLBin execution):
index=* sourcetype=Sysmon EventCode=1
(ParentImage="*\\winword.exe" OR ParentImage="*\\excel.exe" OR ParentImage="*\\outlook.exe" OR ParentImage="*\\acrord32.exe")
(Image="*\\powershell.exe" OR Image="*\\cmd.exe" OR Image="*\\mshta.exe" OR Image="*\\wscript.exe" OR Image="*\\regsvr32.exe")
| table _time host User ParentImage Image CommandLine
DeviceProcessEvents
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","outlook.exe","acrord32.exe")
| where FileName in~ ("powershell.exe","cmd.exe","mshta.exe","wscript.exe","regsvr32.exe")
| project Timestamp,DeviceName,AccountName,InitiatingProcessFileName,FileName,ProcessCommandLine
Encoded / download-cradle PowerShell:
index=* (EventCode=4104 OR (sourcetype=Sysmon EventCode=1))
(CommandLine="*-enc*" OR CommandLine="*-e *" OR CommandLine="*FromBase64String*"
OR CommandLine="*DownloadString*" OR CommandLine="*IEX*" OR CommandLine="*Invoke-Expression*"
OR CommandLine="*Net.WebClient*" OR CommandLine="*Invoke-WebRequest*")
| table _time host User CommandLine
FP notes: software deployment (SCCM), admin scripts, and installers legitimately use PowerShell/cmd from odd parents — baseline your management tooling and exclude it by path/signer, not by turning the hunt off. Tells: Office/PDF → shell, base64 blobs, certutil/bitsadmin downloads, unusual grandparent chains.
12. Hunting: Persistence
# scheduled tasks
index=* (EventCode=4698 OR (sourcetype=Sysmon EventCode=1 Image="*\\schtasks.exe" CommandLine="*/create*"))
| table _time host User Task_Name CommandLine
# services (both logs)
index=* (EventCode=7045 OR EventCode=4697) | table _time host Service_Name Service_File_Name
# Run keys / autoruns (Sysmon 13)
index=* sourcetype=Sysmon EventCode=13 (TargetObject="*\\CurrentVersion\\Run*" OR TargetObject="*\\RunOnce*")
| table _time host Image TargetObject Details
# WMI event subscription (19/20/21)
index=* sourcetype=Sysmon (EventCode=19 OR EventCode=20 OR EventCode=21) | table _time host Operation Consumer Filter
# startup folder drops (Sysmon 11)
index=* sourcetype=Sysmon EventCode=11 TargetFilename="*\\Start Menu\\Programs\\Startup\\*"
# new local user / group add
index=* EventCode=4720 | table _time host Target_User_Name
index=* (EventCode=4732 OR EventCode=4728 OR EventCode=4756) | table _time host Group_Name Member_Name
DeviceRegistryEvents | where RegistryKey has @"\CurrentVersion\Run"
| project Timestamp,DeviceName,RegistryKey,RegistryValueName,RegistryValueData,InitiatingProcessFileName
Your account-manipulation example (folded in): the actor's
net user helpdesk_1 Password123 /add
net localgroup Administrators helpdesk_1 /add
net localgroup "Remote Desktop Users" helpdesk_1 /add
surfaces as 4720 (user helpdesk_1 created) + 4732 (added to Administrators / Remote Desktop Users) + the raw net.exe command lines. If the question is "what did the actor do," enumerate candidates (username helpdesk_1, password Password123, group names) into in.txt; the answer here is the new username helpdesk_1. FP notes: legit admin/onboarding creates users and tasks too — pivot on who and when (off-hours, from a compromised session) to separate.
13. Hunting: Privilege Escalation & Defense Evasion
# admin/special-privilege logons
index=* EventCode=4672 | stats count by Account_Name | sort -count
# UAC-bypass tells (auto-elevate binaries spawning shells)
index=* sourcetype=Sysmon EventCode=1
(ParentImage="*\\fodhelper.exe" OR ParentImage="*\\eventvwr.exe" OR ParentImage="*\\sdclt.exe" OR ParentImage="*\\computerdefaults.exe")
| table _time host User ParentImage Image CommandLine
# token/service privesc tooling
index=* sourcetype=Sysmon EventCode=1 (Image="*PrintSpoofer*" OR CommandLine="*Potato*" OR CommandLine="*SeImpersonate*")
# LOG CLEARED — high signal
index=* (EventCode=1102 OR EventCode=104) | table _time host Account_Name
# AV tampering / exclusions
index=* (CommandLine="*Set-MpPreference*" OR CommandLine="*DisableRealtimeMonitoring*"
OR CommandLine="*Add-MpPreference*Exclusion*" OR CommandLine="*sc * stop *Sense*")
# AMSI / ETW tamper tells (ScriptBlock content)
index=* EventCode=4104 (ScriptBlock="*amsiInitFailed*" OR ScriptBlock="*System.Management.Automation.AmsiUtils*"
OR ScriptBlock="*[Ref].Assembly.GetType*")
# timestomping / file deletion
index=* sourcetype=Sysmon (EventCode=2 OR EventCode=23)
Tells: 1102/104 (cleared logs) is one of the highest-signal indicators in Windows — an actor covering tracks. AV-disable and exclusion-add commands are near-certain malicious in a normal environment. FP notes: patch tools and some installers touch Defender settings — baseline them.
14. Hunting: Credential Access
# LSASS handle by non-system process (Sysmon 10) — the strongest cred-dump tell
index=* sourcetype=Sysmon EventCode=10 TargetImage="*\\lsass.exe"
NOT (SourceImage="*\\MsMpEng.exe" OR SourceImage="*\\wininit.exe" OR SourceImage="*\\csrss.exe"
OR SourceImage="*\\services.exe" OR SourceImage="*\\lsass.exe")
| table _time host SourceImage GrantedAccess CallTrace
# comsvcs / procdump minidump of lsass
index=* sourcetype=Sysmon EventCode=1 (CommandLine="*lsass*" AND
(CommandLine="*MiniDump*" OR CommandLine="*comsvcs.dll*" OR CommandLine="*-ma *" OR CommandLine="*procdump*"))
# mimikatz keywords (works on renamed binaries)
index=* (CommandLine="*sekurlsa*" OR CommandLine="*logonpasswords*" OR CommandLine="*privilege::debug*"
OR CommandLine="*lsadump*" OR CommandLine="*dcsync*")
# SAM/SYSTEM/NTDS theft
index=* (CommandLine="*reg save*sam*" OR CommandLine="*reg save*system*"
OR CommandLine="*shadowcopy*" OR CommandLine="*ntdsutil*" OR CommandLine="*ntds.dit*")
DeviceEvents | where ActionType == "OpenProcessApiCall" and FileName == "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsMpEng.exe","wininit.exe","csrss.exe","services.exe")
Your creds.exe = mimikatz example (folded in): "C:\Windows\Tasks\creds.exe" privilege::debug "sekurlsa::minidump lsass.dmp" sekurlsa::logonpasswords exit → hunt the sekurlsa/minidump keywords plus the lsass.dmp filename (a common answer — the dump name). The GrantedAccess value on EID 10 (0x1010, 0x1410, 0x143a) distinguishes read-for-dump from benign handles.
15. Hunting: Lateral Movement
# remote logons: type 3 (network/SMB) and type 10 (RDP)
index=* EventCode=4624 (Logon_Type=3 OR Logon_Type=10)
| table _time host Account_Name Source_Network_Address Logon_Type
# pass-the-hash / overpass tell: type 9 NewCredentials + NTLM
index=* EventCode=4624 Logon_Type=9 (Authentication_Package=NTLM OR Logon_Process=seclogo)
# PsExec / service-based remote exec
index=* (EventCode=7045 Service_File_Name="*PSEXESVC*") OR (sourcetype=Sysmon EventCode=1 Image="*\\PSEXESVC*")
# WMI / WinRM remote exec (suspicious parents)
index=* sourcetype=Sysmon EventCode=1 (ParentImage="*\\wmiprvse.exe" OR ParentImage="*\\wsmprovhost.exe" OR ParentImage="*\\services.exe" Image="*\\cmd.exe")
# admin share access
index=* (EventCode=5140 OR EventCode=5145) Share_Name="*\\C$" OR Share_Name="*\\ADMIN$"
Pivot: a remote logon gives you a source IP + account → hunt that pair across every host to trace spread; overlay timestamps to order the movement. Tells: one account authenticating (type 3/10) to many hosts in a short window; PSEXESVC/wsmprovhost parentage; type-9 NTLM logons. FP notes: admins, vuln scanners, and management tools produce lots of type-3 — baseline service accounts and scanner IPs.
16. Hunting: Discovery & Collection
# recon command bursts
index=* (CommandLine="*whoami*" OR CommandLine="*net group*" OR CommandLine="*net user*"
OR CommandLine="*net localgroup*" OR CommandLine="*nltest*" OR CommandLine="*ipconfig*"
OR CommandLine="*systeminfo*" OR CommandLine="*Get-Domain*" OR CommandLine="*SharpHound*"
OR CommandLine="*ADRecon*" OR CommandLine="*arp -a*" OR CommandLine="*route print*")
| stats count values(CommandLine) as cmds by host User | where count > 4
# staging / archiving before exfil
index=* (CommandLine="*.zip*" OR CommandLine="*7z*" OR CommandLine="*rar a*"
OR CommandLine="*Compress-Archive*" OR CommandLine="*makecab*")
| table _time host User CommandLine
A burst of discovery commands from one account in minutes is a strong human-operator tell (vs. steady automated noise). Archiving (securitytools.zip in your example) usually precedes exfil — pivot straight to §17. FP notes: IT inventory and login scripts run ipconfig/systeminfo — the cluster and actor context matter more than any single command.
17. Hunting: Command & Control + Exfiltration
# beaconing: regular connections to one destination (Sysmon 3)
index=* sourcetype=Sysmon EventCode=3 dest_is_external=true
| stats count values(DestinationPort) as ports dc(_time) as hits by host DestinationIp
| sort -count
# beacon interval regularity (low jitter = C2)
index=* sourcetype=Sysmon EventCode=3 DestinationIp=<ip>
| sort _time | streamstats current=f last(_time) as prev by host
| eval delta=_time-prev | stats avg(delta) stdev(delta) by host DestinationIp
# suspicious DNS (long/high-entropy = DGA/tunnel)
index=* sourcetype=Sysmon EventCode=22 | eval len=len(QueryName) | where len>40 | table _time host QueryName
# download/upload tooling
index=* (CommandLine="*curl*" OR CommandLine="*Invoke-WebRequest*" OR CommandLine="*Invoke-RestMethod*"
OR CommandLine="*bitsadmin*transfer*" OR CommandLine="*wget*" OR CommandLine="*scp*" OR CommandLine="*rclone*")
# large outbound (proxy/firewall)
index=* sourcetype=proxy | stats sum(bytes_out) as out by src_ip dest | sort -out
Beacon math: low stdev of inter-connection delta + similar byte sizes to one external IP/domain = a C2 heartbeat (§24 walks it). Tells: fixed-interval small connections, high-entropy DNS, new external IP right after a Collection event, rclone/scp/cloud-storage uploads. Follow: archive → upload command → destination.
18. Hunting Active Directory Attacks (detection counterpart)
The blue-team side of the AD attacks. Each offensive technique leaves telemetry:
Kerberoasting (T1558.003):
index=* EventCode=4769 Ticket_Encryption_Type=0x17 Service_Name!="krbtgt" Service_Name!="*$"
| stats count dc(Service_Name) as svc by Account_Name | where svc > 5 OR count > 10
Many 4769 (TGS) requests with RC4 (0x17) from one account, especially for many SPNs, in a short window.
AS-REP Roasting (T1558.004): 4768 (AS-REQ) with pre-auth not required / encryption type RC4 for users flagged DONT_REQUIRE_PREAUTH.
DCSync (T1003.006):
index=* EventCode=4662 Properties="*1131f6aa-9c07-11d1-f79f-00c04fc2dcd2*"
Account_Name!="*$" NOT (Account_Name IN (<known-DC-accounts>))
| table _time host Account_Name
An account that isn't a DC requesting directory replication (the DS-Replication-Get-Changes GUID) = DCSync. Very high signal.
Golden/Silver Ticket (T1558.001/.002): TGS/TGT usage with anomalous lifetimes, encryption downgrades, tickets for accounts that don't exist, or 4624/4634 mismatches; a TGT not preceded by a 4768 on the DC. Baseline normal ticket lifetimes.
Overpass/Pass-the-Hash (T1550): 4624 type 9 (NewCredentials) with NTLM; 4776 NTLM validations where Kerberos is expected.
Delegation abuse / RBCD (T1558): writes to msDS-AllowedToActOnBehalfOfOtherIdentity (directory-change 5136); unusual S4U ticket requests; a new machine account (4741) followed by delegation writes.
ADCS abuse (ESC1/8): certificate requests (4886/4887) with a SAN that differs from the requester; CA enrollment spikes; coercion (spoolss/PetitPotam) followed by cert issuance.
Coercion (PrinterBug/PetitPotam): DC authenticating (type 3) to a non-DC host shortly before ticket/cert abuse; spooler RPC access. Hardening pointers for the report: AES-only, gMSA, pre-auth required, restrict replication rights, MachineAccountQuota=0, SID filtering on trusts, CA template hardening.
19. IOCs, Threat Intelligence & the Pyramid of Pain
IOC types to extract & pivot on: file hashes (SHA256/MD5), filenames & paths, IPs/domains/URLs, registry keys, named pipes, mutexes, service/task names, JA3/JA3S, created usernames, user-agents.
Pyramid of Pain (why behavior beats hashes):
TTPs <- hardest for adversary to change (hunt these for durable detection)
Tools
Network/Host Artifacts
Domain Names
IP Addresses
Hash Values <- trivial to change
Hunt behavior (an LSASS handle, a spray→success→persistence pattern) — it survives a recompile; hashes don't.
Turn one IOC into the whole story:
index=* (Hashes="*<sha256>*" OR CommandLine="*<filename>*" OR DestinationIp="<ip>" OR QueryName="*<domain>*")
| table _time host User Image CommandLine ParentImage
# then pivot on host + user + time window across sources
Enrich with VirusTotal / MISP / vendor reports to attribute tooling: mimikatz→cred access, nxc(NetExec)→enum/lateral, winPEAS→privesc enum, Rubeus/tickets.exe→Kerberos, SharpHound→AD recon, rclone→exfil. Maintain an IOC lookup (inputlookup iocs.csv) and join it against live telemetry.
20. ELK / Elastic & Zeek Network Hunting
Elastic (Kibana KQL / Lucene / EQL):
process.parent.name:"winword.exe" and process.name:("powershell.exe" or "cmd.exe")
event.code:"4104" and powershell.script_block_text:*FromBase64*
// EQL ordered sequence
sequence by host.id with maxspan=1m
[process where process.name=="powershell.exe" and process.command_line:"*DownloadString*"]
[network where destination.port==443]
Zeek logs (network ground truth):
conn.log src/dst/port/bytes/duration -> beacon & exfil
dns.log query/answer -> DGA, long subdomains, tunneling
http.log uri/user_agent/method -> tool downloads, odd UAs
ssl.log server_name/ja3/ja3s -> C2 over TLS, rare client fingerprints
x509.log cert subject/issuer -> self-signed C2 certs
files.log transferred files + hashes -> malware delivery
cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p orig_bytes resp_bytes duration | sort | uniq -c | sort -rn | head # top talkers
cat dns.log | zeek-cut query | awk '{print length,$0}' | sort -rn | head # longest (DGA) queries
cat ssl.log | zeek-cut server_name ja3 | sort | uniq -c | sort -n | head # rarest JA3 = suspicious
cat http.log | zeek-cut user_agent | sort | uniq -c | sort -n | head # rare UAs (tools)
Beaconing analysis: group conn.log by orig_h→resp_h:port; compute inter-connection interval and byte-size variance — low jitter + uniform size = C2 heartbeat. Suricata eve.json gives signature alerts (ET rules) to pivot from. Tells: rare JA3, high-entropy/long DNS, fixed-interval small flows, large orig_bytes to a new external host after a Collection event.
21. Baselining, Correlation & Timeline Building
Baselining / known-good removal — subtract the legitimate so anomalies surface:
index=* sourcetype=Sysmon EventCode=1 | stats count by Image | sort count # long-tail = rare = suspicious
index=* EventCode=4104 | stats count by ScriptBlockText | sort count
... | search NOT (Image="*\\Program Files\\*" OR Image="*\\Windows\\System32\\*" OR User="*SYSTEM*") # after verifying good
Long-tail / stack-counting: the least-common command lines, parent/child pairs, ports, and destinations are where actors hide — sort ascending, investigate the rare.
Correlation (join stages):
# host session grouping
index=* host=WKS01 | transaction host maxpause=5m
# tie a logon to subsequent process activity by the same account
index=* (EventCode=4624 OR sourcetype=Sysmon EventCode=1) Account_Name=jdoe | sort _time
| table _time host EventCode Logon_Type Image CommandLine
# join network to process by ProcessGuid
index=* sourcetype=Sysmon (EventCode=1 OR EventCode=3) | transaction ProcessGuid
Timeline building: normalize timestamps (eval t=strftime(_time,"%F %T")), sort ascending, lay events in ATT&CK order, and bound the intrusion by patient zero (first malicious event) and the last (exfil/impact). Every row: time · host · user · action · ATT&CK ID · evidence. This table is the report's backbone and answers most exam questions directly.
22. Worked Hunt #1 — File IOC → Full Kill-Chain
From one IOC to the whole intrusion, query + ATT&CK per step. All detection.
Lead: intel flags a hash for nxc.exe in the environment.
1 — Confirm & anchor.
index=* sourcetype=Sysmon EventCode=1 (Image="*nxc.exe*" OR Hashes="*<sha256>*")
| table _time host User Image CommandLine ParentImage Hashes
→ nxc.exe ran on WKS01 as j.doe at 09:14, parent powershell.exe. Anchor = WKS01 + j.doe + ~09:14.
2 — Initial access/execution (pivot back).
index=* sourcetype=Sysmon EventCode=1 host=WKS01 | sort _time
| search (ParentImage="*\\winword.exe" OR CommandLine="*DownloadString*" OR CommandLine="*-enc*")
→ winword.exe → powershell -enc <b64> at 09:02 (T1566 → T1059.001). Patient zero.
3 — Credential access.
index=* sourcetype=Sysmon (EventCode=10 TargetImage="*\\lsass.exe") OR (EventCode=1 CommandLine="*sekurlsa*")
host=WKS01 | table _time SourceImage CommandLine
→ creds.exe opened an LSASS handle at 09:10, dumped lsass.dmp (T1003.001). IOC: lsass.dmp.
4 — Lateral movement (pivot on account+source).
index=* EventCode=4624 (Logon_Type=3 OR Logon_Type=10) Account_Name=j.doe
| table _time host Source_Network_Address Logon_Type
→ network logon WKS01→SRV02 at 09:22, then PSEXESVC (7045) on SRV02 (T1021.002 / T1569.002).
5 — Collection & exfil.
index=* host=SRV02 (CommandLine="*Compress-Archive*" OR CommandLine="*.zip*")
index=* sourcetype=Sysmon EventCode=3 host=SRV02 | stats count by DestinationIp
→ securitytools.zip at 09:31 (T1560), large outbound to 185.x.x.x:443 at 09:34 (T1041).
Timeline & answers:
09:02 WKS01 winword->powershell -enc T1566/T1059.001 (patient zero)
09:10 WKS01 creds.exe -> lsass.dmp T1003.001 (dump filename)
09:14 WKS01 nxc.exe enum/lateral T1087/T1021
09:22 WKS01->SRV02 network logon T1021.002 (spread path)
09:24 SRV02 PSEXESVC service install T1569.002
09:31 SRV02 securitytools.zip staged T1560
09:34 SRV02 -> 185.x.x.x:443 exfil T1041 (exfil IP)
Every exam answer (patient-zero time, dump name, spread path, exfil IP) falls out of the timeline. Convert each detection into a saved search.
23. Worked Hunt #2 — Logon-Spray Spike
Lead from an auth anomaly, not a file. All detection.
Lead: a spike in 4625 failures from one source.
index=* EventCode=4625 | stats count dc(Target_User_Name) as users by Source_Network_Address
| where count>50 | sort -count
→ 300+ failures from 10.0.0.66 across 40 users (T1110). Anchor = 10.0.0.66.
Find the success:
index=* EventCode=4624 Source_Network_Address=10.0.0.66 | table _time Target_User_Name Logon_Type Workstation_Name
→ one 4624 for svc_backup at 11:07 (T1078).
Follow the account:
index=* (sourcetype=Sysmon EventCode=1 OR EventCode=4688) User=*svc_backup* | sort _time | table _time host Image CommandLine
→ whoami /priv, net group "Domain Admins" /domain, then schtasks /create (T1087 + T1053.005).
Scope the persistence:
index=* (EventCode=4698 OR (sourcetype=Sysmon EventCode=1 Image="*schtasks.exe" CommandLine="*/create*"))
| table _time host User Task_Name CommandLine
→ task \Microsoft\Windows\UpdateSync on SRV05, runs a payload every 30 min.
11:00 10.0.0.66 spray (40 users) T1110
11:07 10.0.0.66 success -> svc_backup T1078
11:09 SRV05 discovery T1087
11:12 SRV05 schtasks UpdateSync T1053.005
Lesson: an auth anomaly is a valid lead — pivot source IP → successful account → the account's activity. Recommend locking svc_backup, removing the task, adding a spray saved search.
24. Worked Hunt #3 — Beaconing / C2 Lead
Lead from network regularity. All detection.
Lead: proxy shows one internal host with steady small connections to a new external IP.
1 — Confirm regularity (beacon math):
index=* sourcetype=Sysmon EventCode=3 DestinationIp=<ip>
| sort _time | streamstats last(_time) as prev by host | eval delta=_time-prev
| stats count avg(delta) as avg_int stdev(delta) as jitter avg(...) by host DestinationIp
→ 240 connections, avg interval ~60s, low jitter — a beacon (T1071). Anchor = the beaconing host + dest IP.
2 — What process is beaconing?
index=* sourcetype=Sysmon EventCode=3 DestinationIp=<ip> | stats count by Image | sort -count
→ svchost.exe from an unusual path (or an injected process) → pivot to its creation (EID 1) and parent.
3 — How did it get there (pivot back)? Trace the process's ProcessGuid to its EID 1, then its parent chain → an earlier download-cradle PowerShell / macro (initial access).
4 — What did it do (pivot forward)? From the same host/user, hunt discovery, credential access, lateral movement in the beacon's active window.
5 — Exfil check:
index=* sourcetype=proxy dest=<c2> | stats sum(bytes_out) as out by src_ip | sort -out
→ a spike in bytes_out right after a Compress-Archive = staged exfil over the C2 channel (T1041/T1567). Answers: C2 IP/domain, beaconing process, interval, first-seen (patient zero), bytes exfiltrated.
25. Worked Hunt #4 — Data Staging & Exfiltration
Lead from a Collection artifact. All detection.
Lead: a large archive appears in a temp/public path.
index=* sourcetype=Sysmon EventCode=11 (TargetFilename="*.zip" OR TargetFilename="*.7z" OR TargetFilename="*.rar")
(TargetFilename="*\\Temp\\*" OR TargetFilename="*\\Public\\*" OR TargetFilename="*\\Downloads\\*")
| table _time host Image TargetFilename
→ db_backup.7z created on SRV02 by 7z.exe at 14:10.
1 — What went into it (pivot back to Collection)? Hunt file reads / access to sensitive shares (5145) and Compress-Archive/7z a command lines on SRV02 before 14:10 → the actor pulled from a DB export directory (T1005/T1039).
2 — Who staged it? Correlate the 7z.exe ProcessGuid → parent process → the session/account (a prior lateral logon from §23-style movement).
3 — Where did it go (pivot forward to exfil)?
index=* host=SRV02 (CommandLine="*rclone*" OR CommandLine="*curl*-T*" OR CommandLine="*Invoke-RestMethod*Put*"
OR CommandLine="*scp*db_backup*")
index=* sourcetype=proxy src_ip=<SRV02> | stats sum(bytes_out) as out by dest | sort -out
→ rclone upload to a cloud-storage endpoint at 14:22, large bytes_out (T1567.002). Answers: archive name, staging path, source data, exfil tool + destination, byte volume, timeline. Recommend DLP + egress controls + the saved searches for archive-creation and rclone usage.
26. Detection Engineering — Saved Searches, Rules, Sigma
The deliverable of a hunt is reusable coverage. Convert findings into scheduled saved searches (Splunk), analytic rules (Sentinel), or portable Sigma.
Splunk scheduled saved search — LSASS access:
index=* sourcetype=Sysmon EventCode=10 TargetImage="*\\lsass.exe"
NOT SourceImage IN ("*\\MsMpEng.exe","*\\wininit.exe","*\\csrss.exe","*\\services.exe")
| table _time host SourceImage GrantedAccess # schedule 15m; alert if > 0
Password-spray rule:
index=* EventCode=4625 | bin _time span=10m
| stats dc(Target_User_Name) as users by _time Source_Network_Address | where users > 15
DCSync rule:
index=* EventCode=4662 Properties="*1131f6aa-9c07-11d1-f79f-00c04fc2dcd2*" Account_Name!="*$"
Sentinel analytic (KQL) — encoded PowerShell from Office:
DeviceProcessEvents
| where ProcessCommandLine has_any ("-enc","-EncodedCommand","FromBase64String")
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","outlook.exe","mshta.exe")
Sigma (portable → SPL/KQL/Elastic via sigmac/pySigma):
title: Suspicious LSASS Process Access
logsource: {product: windows, service: sysmon}
detection:
sel: {EventID: 10, TargetImage|endswith: '\lsass.exe'}
filt: {SourceImage|endswith: ['\MsMpEng.exe','\wininit.exe','\csrss.exe']}
condition: sel and not filt
level: high
tags: [attack.credential_access, attack.t1003.001]
Habit: for every technique you hunted, write "the saved search that catches it at hour zero," tag it with the ATT&CK ID, and track coverage on the ATT&CK matrix (Navigator layer). Detection engineering is how hunting compounds.
27. Hunt-Hypothesis Library
Ready-made hypotheses (lead → data → query focus). Use as a hunt backlog.
H1 "An actor dumped LSASS." Sysmon 10 handle to lsass; comsvcs MiniDump (T1003.001)
H2 "Office delivered a payload." Office->shell parent/child; 4104 cradles (T1566/T1059)
H3 "Persistence via scheduled task." 4698 / schtasks /create off-hours (T1053.005)
H4 "A new admin account was added." 4720 + 4732 outside change window (T1136/T1098)
H5 "Kerberoasting occurred." 4769 RC4 bursts, many SPNs (T1558.003)
H6 "DCSync from a non-DC." 4662 replication GUID, non-DC account (T1003.006)
H7 "Lateral movement via SMB/WMI." 4624 type3 to many hosts; PSEXESVC; wsmprovhost (T1021)
H8 "Pass-the-hash in use." 4624 type 9 NTLM (T1550.002)
H9 "C2 beaconing." Sysmon 3 low-jitter intervals to new IP (T1071)
H10 "Data staged for exfil." archive creation in Temp/Public + rclone/scp (T1560/T1567)
H11 "Logs were cleared." 1102 / 104 (T1070.001)
H12 "LOLBin download." certutil urlcache / bitsadmin / mshta http (T1105)
H13 "AMSI/Defender tampering." 4104 amsiInitFailed; Set-MpPreference disable (T1562.001)
H14 "DNS tunneling / exfil." Sysmon 22 long/high-entropy queries (T1048/T1071.004)
H15 "RBCD / delegation abuse." 5136 msDS-AllowedToActOnBehalf; new machine acct (T1558)
Run each against your telemetry, record found/not-found + a new detection, and note any you can't run (a coverage gap = a finding).
28. False Positives & Tuning
Every hunt returns noise; tuning is what makes detections usable.
Baseline first. Know your admin tooling (SCCM/Intune, RMM, vuln scanners, backup agents) — they legitimately spawn shells, touch Defender, run
net/ipconfig, and generate type-3 logons. Exclude by signer/path/known-account, not by dropping the hunt.Exclude precisely. Prefer
NOT SourceImage IN (...)over broadNOT *system32*(actors live in System32 too). Scope, don't blind.Context over keywords.
certutilis fine;certutil -urlcache http://...is not. Hunt the combination (binary + argument + parent + path + time).Off-hours & rarity weighting. The same command is more suspicious at 03:00 from a workstation than at 10:00 from an admin jump box.
Threshold with care. Spray rules need
dc(user) > Nsized to your environment; too low = noise, too high = misses low-and-slow.Suppress known-good, review the rest. Maintain an allow-list lookup and re-baseline periodically — environments drift.
Validate detections. Test each saved search against a known-good week (false-positive rate) and, where safe, an atomic-red-team-style benign simulation (true-positive rate).
Document tuning decisions — "excluded MsMpEng from LSASS-access rule" is part of the report and keeps the detection honest.
29. Linux & macOS Threat Hunting
Windows dominates OSTH, but intrusions cross platforms. The same broad→narrow→confirm→pivot loop applies to *nix telemetry.
Linux data sources:
auditd (/var/log/audit/audit.log) syscalls: execve, connect, open, setuid (the core)
Sysmon for Linux process(1)/network(3)/file(11) like Windows Sysmon
/var/log/auth.log|secure SSH/sudo/su auth
/var/log/syslog, journalctl service & system events
bash/zsh history, /root/.*_history command history (often cleared -> a tell)
osquery SQL over live host state (processes, sockets, users, crontab)
High-value Linux hunts:
# suspicious execve (auditd) — downloads, reverse shells, enum
ausearch -m execve | grep -E "curl|wget|nc |ncat|/dev/tcp|base64 -d|python -c|chmod \+x"
# persistence: cron, systemd, rc, authorized_keys, LD_PRELOAD
grep -R . /etc/cron* /var/spool/cron 2>/dev/null
find / -name authorized_keys -newermt "-2 days" 2>/dev/null
ls -la /etc/systemd/system /etc/rc.local
grep -R "LD_PRELOAD" /etc 2>/dev/null
# privilege escalation tells
ausearch -m execve | grep -E "sudo|pkexec|setuid|chmod 4|chmod \+s"
# SSH lateral / brute
grep -E "Accepted|Failed" /var/log/auth.log | awk '{print $1,$2,$3,$9,$11}'
Sysmon-for-Linux (SPL):
index=* sourcetype=sysmon:linux EventCode=1 (CommandLine="*/dev/tcp*" OR CommandLine="*nc -e*"
OR CommandLine="*base64 -d*" OR CommandLine="*chmod +x*" OR CommandLine="*curl*|*sh*")
| table _time host user Image CommandLine
osquery (portable live hunting):
SELECT name,path,cmdline,parent FROM processes WHERE on_disk=0; -- deleted-binary procs (malware tell)
SELECT * FROM crontab; -- cron persistence
SELECT * FROM authorized_keys; -- SSH key persistence
SELECT * FROM listening_ports lp JOIN processes p ON lp.pid=p.pid; -- listeners -> C2/backdoor
SELECT * FROM users WHERE uid=0 AND username!='root'; -- extra uid-0 accounts
macOS: Unified Log (log show), ESF/EDR telemetry, LaunchAgents/LaunchDaemons (~/Library/LaunchAgents, /Library/LaunchDaemons) for persistence, com.apple masquerading. *Tells (all nix): deleted-but-running binaries, world-writable cron, new authorized_keys, uid-0 additions, reverse-shell command shapes, cleared history.
30. Cloud & Identity Hunting (Entra / M365 / AWS)
Identity is the modern perimeter; many intrusions now live in sign-in and audit logs, not on endpoints.
Microsoft Entra ID / M365 (KQL):
// impossible travel / anomalous sign-in
SigninLogs
| where ResultType == 0
| summarize locs=make_set(Location), ips=make_set(IPAddress) by UserPrincipalName, bin(TimeGenerated,1h)
| where array_length(locs) > 1
// legacy-auth / MFA-bypass (legacy protocols ignore MFA)
SigninLogs | where ClientAppUsed in ("IMAP4","POP3","SMTP","Other clients")
// suspicious OAuth consent (illicit app grant)
AuditLogs | where OperationName == "Consent to application"
// new service principal credential (backdoor)
AuditLogs | where OperationName in ("Add service principal credentials","Update application – Certificates and secrets management")
// mass file access / download (exfil in M365)
CloudAppEvents | where ActionType in ("FileDownloaded","FileSyncDownloadedFull") | summarize count() by AccountDisplayName, bin(Timestamp,1h) | where count_ > 100
// inbox rule creation (BEC persistence)
CloudAppEvents | where ActionType == "New-InboxRule"
AWS CloudTrail (hunt in Splunk/Athena/OpenSearch):
index=cloudtrail (eventName=ConsoleLogin errorMessage=*Failed*) # brute/spray
index=cloudtrail eventName IN (CreateUser,CreateAccessKey,AttachUserPolicy,PutUserPolicy) # persistence/privesc
index=cloudtrail eventName IN (GetSecretValue,GetParameter,Decrypt) # secret access
index=cloudtrail eventName IN (CreateTrail,StopLogging,DeleteTrail) # log tampering (evasion!)
index=cloudtrail userIdentity.type=Root # root usage (should be rare)
index=cloudtrail eventName IN (RunInstances,CreateLoginProfile) userAgent=*python* # scripted abuse
Cloud tells: sign-in from a new ASN/country then privileged action; legacy-auth success; a new SP credential or OAuth consent; StopLogging/DeleteTrail (the cloud equivalent of clearing logs — very high signal); root or new-access-key usage; bursts of GetSecretValue. Pivot: identity → the resource actions that identity took → data touched. Hardening pointers for the report: block legacy auth, admin-consent workflow, Conditional Access + MFA, CloudTrail integrity/immutability, least-privilege roles.
31. Live Response & Triage
Sometimes you hunt the live host, not just the SIEM — confirming a lead or scoping fast.
Windows quick triage:
Get-CimInstance Win32_Process | select ProcessId,ParentProcessId,Name,CommandLine # running procs + args
Get-NetTCPConnection | ? State -eq Established | select LocalAddress,RemoteAddress,RemotePort,OwningProcess
Get-ScheduledTask | ? {$_.State -ne 'Disabled'} # tasks
Get-CimInstance Win32_StartupCommand # autoruns
Get-LocalUser; Get-LocalGroupMember Administrators # accounts
Get-WinEvent -FilterHashtable @{LogName='Security';Id=4624} -MaxEvents 50 # recent logons
# Sysinternals: autorunsc -accepteula -a *, tcpview, procexp, sigcheck (unsigned in system dirs)
Volatile-first order (if imaging): memory → network state → processes → logged-on users → open files → disk artifacts. Capture RAM (WinPMEM/DumpIt) before powering off — injected/fileless malware lives only in memory. Memory forensics (Volatility 3):
vol -f mem.raw windows.pslist ; vol -f mem.raw windows.pstree # process tree
vol -f mem.raw windows.malfind # injected/RWX regions
vol -f mem.raw windows.netscan # connections
vol -f mem.raw windows.cmdline ; vol -f mem.raw windows.dlllist
Disk artifacts to pull: $MFT, Amcache.hve/Shimcache (execution evidence), Prefetch (C:\Windows\Prefetch\*.pf), SRUM, event logs, Amcache hashes, browser history, PowerShell transcript/history. Tools: KAPE (collection), Eric Zimmerman's tools (parsing), plaso/log2timeline (super-timeline). Rule: live response confirms and scopes; the SIEM hunt finds and reconstructs. Preserve evidence (hashes, chain-of-custody) as you go.
32. Reporting, Metrics & Glossary
Hunt write-up / report:
1. Executive Summary - what happened, business impact, plain language
2. Scope & Data Sources- hosts, log sources, time window, coverage gaps
3. Timeline - chronological, ATT&CK-mapped: initial access -> ... -> exfil (with diagram)
4. Findings / IOCs - hashes, accounts, paths, IPs, tasks (table)
5. Affected Assets - hosts & accounts touched
6. Detections & Recommendations - new saved searches/rules + hardening per technique
7. Appendices - raw queries, event excerpts, tuning notes
Tips: anchor every claim to a specific event (time + host + EventCode/ActionType); map each step to ATT&CK; note exact answer-values (hashes, helpdesk_1, lsass.dmp, exfil IP) as you find them; turn confirmed detections into saved searches and cite them.
Hunt metrics (maturity): techniques hunted vs. ATT&CK coverage, new detections created per hunt, mean time to detect (MTTD) improvement, dwell-time of findings, false-positive rate of shipped rules.
Glossary:
Threat hunting — proactive, hypothesis-led search for adversary activity past existing alerts.
IOC / IOA — indicator of compromise (artifact) / of attack (behavior).
Sysmon — Sysinternals driver giving rich process/network/registry/DNS/image-load telemetry.
SPL / KQL / EQL — Splunk / Kusto (Defender-Sentinel) / Elastic query languages; EQL does ordered sequences.
CIM — Splunk Common Information Model (normalized field names across sources).
MITRE ATT&CK — tactic/technique taxonomy hunts and reports map to.
LOLBin / LOLBAS / GTFOBins — living-off-the-land binaries and their reference catalogs.
Pivot — following an anchor (host/user/time/hash/GUID/IP) to the next kill-chain stage.
Pyramid of Pain — how costly each IOC type is for an adversary to change (hashes easy, TTPs hard).
Beaconing — regular, low-jitter C2 check-ins detectable by interval/size analysis.
Baseline / long-tail / stack-counting — establishing normal so rare = suspicious.
Kill-chain / timeline — the reconstructed sequence of attacker actions.
Detection engineering — converting hunt findings into durable saved searches / analytic rules / Sigma.
End of guide. OSTH/TH-200 is defensive throughout: every query finds attacker activity in telemetry. Win by hunting broad → narrow → confirm → pivot, mapping each step to ATT&CK, reconstructing the timeline, and answering with the exact artifact — using a bulk checker like run.ps1 only to confirm candidates your hunting already produced. Then turn every finding into a detection so the next intrusion trips an alert at hour zero.