Hunting for Secondary Compromise
How attackers move from the initially identified scope to hosts you haven't found yet — lateral movement artifact hunting, credential reuse detection, and how to bound the true blast radius before declaring eradication complete.
You've scoped the incident to 5 confirmed compromised hosts. You've contained them, removed persistence, and reset the associated credentials. Eradication is declared at day 4. On day 6, your SOC gets an alert: suspicious PowerShell execution on a host in a completely different subnet — one you didn't examine during IR. The EDR telemetry shows the attacker used credentials from the original compromise to move laterally to a host that wasn't in your original blast radius. You missed it because you didn't hunt secondary compromise from the foothold hosts — you only investigated hosts that had alerts. This chapter explains how to bound the true scope from an attacker's perspective, not just the alert perspective.
How Lateral Movement Leaves Evidence
Lateral movement is not silent. Every jump leaves artifacts on both the source host (outbound) and the destination host (inbound), and typically in Active Directory logs too.
Lateral Movement Artifact Map
═══════════════════════════════════════════════════════════════════
SOURCE HOST (host attacker is moving FROM):
Windows Security Log:
4648 — Explicit credential logon (using /netonly or runas)
4688 + cmdline — net use, psexec, wmic, winrm, mmc
Sysmon:
Event 1 (Process Create): psexec.exe, wmic.exe, winrm.exe, net.exe
Event 3 (Network Connect): outbound to new internal IP on ports
445, 5985, 135, 139, 3389
Registry: NTUSER.DAT\Network — mapped drives
DESTINATION HOST (host attacker is moving TO):
Windows Security Log:
4624 Type 3 — Network logon (SMB/WMI/file access)
4624 Type 10 — RemoteInteractive (RDP)
4624 Type 2 — Interactive logon (physical/console)
4672 — Special privileges assigned (if using admin account)
7045 — New service installed (if psexec / service deployment used)
Sysmon:
Event 1: process spawned by psexec parent, WMI provider host, etc.
DOMAIN CONTROLLER:
Event 4769 — Kerberos Service Ticket requested for destination host
Event 4776 — NTLM authentication to destination host
(Both show source IP + account + destination)
Hunting Outbound from the Compromised Host
# On the compromised host (via EDR live response or direct PowerShell):
# Find all internal IPs this host connected to during the incident window
$startTime = [DateTime]"2026-08-01T00:00:00"
$endTime = [DateTime]"2026-08-29T23:59:59"
# Event 4648 — Explicit credential use (attacker using alternate creds)
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = 4648
StartTime = $startTime
EndTime = $endTime
} -ErrorAction SilentlyContinue | ForEach-Object {
$xml = [xml]$_.ToXml()
$data = $xml.Event.EventData.Data
[PSCustomObject]@{
Time = $_.TimeCreated
AccountUsed = $data | Where-Object Name -eq "SubjectUserName" | Select -Expand "#text"
TargetServer= $data | Where-Object Name -eq "TargetServerName" | Select -Expand "#text"
ProcessName = $data | Where-Object Name -eq "ProcessName" | Select -Expand "#text"
}
} | Where-Object { $_.TargetServer -ne "localhost" } | Format-Table
Hunting Lateral Movement from Domain Controller Logs
Domain controller logs are the single best source for lateral movement telemetry — every Kerberos ticket request identifies both the source and destination. This hunt requires pulling logs from all domain controllers, not just the one you happen to query.
# Hunt for Kerberos ticket requests from compromised accounts
# Run on domain controller or via remote event log query
$compromisedAccounts = @("jsmith","mwilliams","svc_deploy")
$startTime = [DateTime]"2026-08-01"
# Event 4769 — Kerberos Service Ticket requested
# ServiceName = the host the ticket was requested for (lateral move destination)
$dcs = (Get-ADDomainController -Filter *).HostName
foreach ($dc in $dcs) {
Write-Host "`n== DC: $dc ==" -ForegroundColor Cyan
Get-WinEvent -ComputerName $dc -FilterHashtable @{
LogName = "Security"
Id = 4769
StartTime = $startTime
} -ErrorAction SilentlyContinue | ForEach-Object {
$xml = [xml]$_.ToXml()
$d = $xml.Event.EventData.Data
$acct = ($d | Where-Object Name -eq "TargetUserName")."#text"
$svc = ($d | Where-Object Name -eq "ServiceName")."#text"
$ip = ($d | Where-Object Name -eq "IpAddress")."#text"
if ($compromisedAccounts -contains $acct.Split("@")[0]) {
[PSCustomObject]@{
Time = $_.TimeCreated
Account = $acct
Service = $svc # host being accessed
SourceIP= $ip
}
}
} | Where-Object { $_ } | Format-Table
}
Credential Reuse Detection
Password reuse across the fleet is an attacker force multiplier. If the attacker cracked a local admin password, they may have reused it to access other machines where the same password is set — which is typically every machine where that local admin account was set up with the same base password.
# Identify machines where compromised accounts had successful logons
# beyond the machines you already know about
$compromisedAccounts = @("jsmith","mwilliams")
$startTime = [DateTime]"2026-08-01"
# Query all machines' Security logs for successful logons by compromised accounts
$computers = Get-ADComputer -Filter * | Select-Object -ExpandProperty Name
$results = Invoke-Command -ComputerName $computers -ScriptBlock {
param($accounts, $start)
Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = @(4624, 4625) # success and failure
StartTime = $start
} -ErrorAction SilentlyContinue | ForEach-Object {
$xml = [xml]$_.ToXml()
$d = $xml.Event.EventData.Data
$acct = ($d | Where-Object Name -eq "TargetUserName")."#text"
$type = ($d | Where-Object Name -eq "LogonType")."#text"
$ip = ($d | Where-Object Name -eq "IpAddress")."#text"
if ($accounts -contains $acct) {
[PSCustomObject]@{
Computer = $env:COMPUTERNAME
Time = $_.TimeCreated
EventId = $_.Id
Account = $acct
LogonType= $type
SourceIP = $ip
}
}
}
} -ArgumentList $compromisedAccounts, $startTime -ErrorAction SilentlyContinue
$results | Where-Object { $_ } | Sort-Object Time | Format-Table
Determining When Scope Is Bounded
You can never prove a negative — that there are zero unidentified compromised hosts. But you can reach a defensible confidence threshold by applying the following criteria.
| Criterion | How to verify |
|---|---|
| All Kerberos/NTLM authentications from compromised accounts are accounted for | DC log query shows all authentication events; each destination host has been examined |
| All destination hosts from lateral movement artifacts have been examined | Every IP from the lateral movement hunt has been checked for persistence and cleared |
| Network flow analysis shows no unaccounted east-west connections from compromised hosts | NetFlow/firewall logs show all internal connections during incident window from compromised IPs |
| No new persistence mechanisms created since containment | EDR telemetry shows no new scheduled task/service/registry changes on any monitored host since containment |
| 48-hour monitoring window post-eradication shows no attacker activity | SIEM hunting, EDR behavior monitoring, new accounts, authentication anomalies — all quiet |
Every day an attacker spends in the environment is another day of potential lateral movement. A dwell time of 1 day means the attacker probably touched only a few hosts. A dwell time of 60 days means they've had time to map the entire environment, extract all valuable credentials, and establish persistence on every high-value system. Before starting the secondary compromise hunt, multiply your initial confirmed scope by the dwell time in days. If you found 3 compromised hosts after 45 days of dwell, expect the real scope to be substantially larger — and design the hunt accordingly.
Q & A
Q: DC logs only go back 30 days due to log size limits. But the compromise started 60 days ago. What do you do?
Check your SIEM first — if Windows Security events are forwarded to the SIEM, the DC log retention in the SIEM may be longer than the on-host retention. If no SIEM has the events: check NetFlow/firewall logs for network-level evidence of lateral movement — they often have longer retention than Windows event logs. Accept that your scope analysis will have a blind spot for the first 30 days and document this limitation in your incident report. For the remaining window, do the best analysis you can with available data. Post-incident: increase DC Security log size and SIEM forwarding to at least 90 days to close this gap for future incidents.
Q: You find a host that shows inbound 4624 type 3 from a compromised account, but the host itself is clean — no persistence, no malware. Does it go on the compromised list?
Yes — but with qualification. The host is "accessed" rather than necessarily "compromised." Document it as: "HOSTNAME — accessed by compromised account [account] on [date]; no persistent tooling found; requires credential review and monitoring." Add it to the eradication scope for credential rotation (any local or service accounts on this host that the attacker could have harvested while logged in) and to the post-incident monitoring list. A "clean" machine that was accessed with compromised credentials is not the same as a confirmed-never-touched machine. The attacker may have accessed it to exfiltrate data, or may have simply connected and found nothing worth persisting on. Either way it counts as within the blast radius.