Lateral Movement Forensics: RDP, PsExec, WMI and WinRM
Map source and destination artifacts for RDP, PsExec, SMB, WMI, WinRM, scheduled tasks and DCOM, then build a cross-host lateral movement graph.
Lateral movement is where a single compromised workstation becomes a domain-wide incident. This guide maps, technique by technique, which artifacts appear on the source host (where the attacker is) and which appear on the destination host (where they land), so you can prove each hop and chain them into a graph. By the end you should be able to pick the right logs for RDP, PsExec, SMB, WMI, WinRM, scheduled tasks and DCOM, and know which indicators suggest stolen hashes or explicit credentials.
If you are new to Windows event logs, start with the event log overview and the logon type reference, since almost every section below depends on them.
The source vs destination mindset
Every hop has two ends. The destination usually logs the most: an authentication (4624), a session, a service or a process creation. The source logs less, and what it logs is often user-side: client history, Prefetch for the tool that was launched, explicit credential use (4648). Investigators who only look at destinations can say "someone got in from 10.1.2.3" but not what that host did next. Investigators who only look at sources miss hops started from tooling that never touched disk.
Two rules keep you honest:
- Pivot on the source IP and the logon ID. The
IpAddressfield of a 4624 on the destination tells you which host to collect next. TheTargetLogonIdlinks that logon to every later event of the same session. - Check your auditing baseline. Several events used here (4688 process creation, 5140/5145 share access, 4698 task creation) only exist if the matching audit subcategory was enabled. Absence of evidence is often absence of auditing.
RDP
RDP is the most visible technique because it creates an interactive session with its own lifecycle events. The RDP connections page has the full field reference.
Destination
- Security 4624, logon type 10 (RemoteInteractive). A reconnect to an existing session, or an unlock, shows type 7. With Network Level Authentication you will often also see a type 3 logon just before the type 10.
- Security 4778 / 4779: session reconnected / disconnected, with client name and address. These are useful for measuring how long a session actually lasted.
- TerminalServices-RemoteConnectionManager/Operational 1149: network authentication succeeded, with user, domain and source IP. It is logged before the session exists, so treat it as "authenticated", not "logged on".
- TerminalServices-LocalSessionManager/Operational: 21 (session logon), 22 (shell start), 24 (disconnected), 25 (reconnected), 23 (logoff). Event 22 is the one that tells you a desktop was actually delivered.
- Process creation under the RDP session (
rdpclip.exe,tstheme.exe) and the usual user activity artifacts for the account on that host.
Source
NTUSER.DAT\Software\Microsoft\Terminal Server Client\DefaultholdsMRU0toMRU9, the servers typed into the client....\Servers\<host>holdsUsernameHint, the account used for that host. See NTUSER.DAT for hive handling.- TerminalServices-RDPClient/Operational 1024 (client attempting to connect to a named server) and 1102 (client initiated a multi-transport connection to an IP).
Documents\Default.rdp(hidden) keeps the settings of the last connection, includingfull address.- The RDP bitmap cache under
%LOCALAPPDATA%\Microsoft\Terminal Server Client\Cache\stores tiles of what the user saw on the remote screen. Reassembled, they can show the attacker's desktop on the destination. - Prefetch and Jump Lists for
mstsc.exe, and a 4648 if alternate credentials were typed.
PsExec and other service-based execution
PsExec copies a service binary to the ADMIN$ share, creates a service through the Service Control Manager, and talks to it over named pipes. Impacket's psexec.py, smbexec.py and many C2 frameworks follow the same pattern with different names.
Destination
- Security 4624 type 3 from the source IP, usually with a 4672 (special privileges) because administrative rights are required. When
-uis given with explicit credentials, you may also see a separate logon for the account the remote process runs as; do not rely on a specific type there. - Security 5140 / 5145: access to
\\*\ADMIN$and toPSEXESVC.exeinside it (needs "File Share" and "Detailed File Share" auditing). - System 7045: service installed, service name
PSEXESVCand image path%SystemRoot%\PSEXESVC.exeby default. With-r <name>, both the service name and binary name change, so hunt on the structure rather than the name. 7036 records the service entering the running and stopped states. - The service key under
HKLM\SYSTEM\CurrentControlSet\Services\(see services), usually deleted when PsExec exits cleanly but sometimes left behind after a crash or kill. - Prefetch for
PSEXESVC.EXE(or the renamed binary) and for whatever was run through it. Child processes have the service binary as parent.
Source
- Prefetch for
PsExec.exe/PsExec64.exe, plus Amcache and ShimCache entries for the binary. NTUSER.DAT\Software\Sysinternals\PsExecwithEulaAccepted, created the first time the user accepted the EULA (bypassed with-accepteula, which still writes it).- 4648 when
-uis used, naming the target server.
SMB and admin shares
Mapping C$ or ADMIN$ is often the step before PsExec, a copy of tools, or data staging.
- Destination: 4624 type 3, 4672 for admins, 5140 (share accessed) and 5145 (file in share accessed), plus
Microsoft-Windows-SMBServeroperational logs on recent builds. New files inC:\Windows\orC:\Windows\Temp\with a creation time matching the logon are the classic follow-up. - Source: Prefetch for
net.exe/net1.exe,MountPoints2entries in NTUSER.DAT for shares mounted through Explorer, ShellBags if the user browsed the share, and 4648 ifnet usewas given credentials.
WMI
Remote WMI (wmic /node:, Invoke-WmiMethod, Invoke-CimMethod, Impacket wmiexec.py) calls Win32_Process.Create on the destination.
- Destination: 4624 type 3 with 4672. Processes spawned by
WmiPrvSE.exeare the key indicator: acmd.exeorpowershell.exewhose parent isWmiPrvSE.exedeserves a look (4688 or Sysmon 1).Microsoft-Windows-WMI-Activity/Operationalrecords provider activity and errors (5857, 5858), and permanent event subscriptions (5861), which is where WMI crosses into persistence (see the WMI repository).wmiexec.pyalso tends to redirect output to a file onADMIN$, so 5145 can help. - Source: Prefetch for
wmic.exeor PowerShell logs showing the WMI cmdlets, and 4648 if credentials were supplied.
WinRM and PowerShell remoting
Enter-PSSession, Invoke-Command and winrs all ride on WinRM (TCP 5985/5986).
Destination
- 4624 type 3 for the connecting account.
wsmprovhost.exeis the host process of the remote session: anything it spawns ran through remoting. Prefetch forWSMPROVHOST.EXEis a quick check that remoting was ever used on the host.- Microsoft-Windows-WinRM/Operational: 91 (creating a shell / request handling), 168 (authentication of the user), among others. Event wording varies by build, so read the message text rather than trusting the ID alone.
- PowerShell:
Microsoft-Windows-PowerShell/Operational4103 (module logging) and 4104 (script block logging, including the remote commands), and the classicWindows PowerShelllog with 400/403 where theHostApplicationorHostNameindicatesServerRemoteHost. See PowerShell artifacts.
Source
- Microsoft-Windows-WinRM/Operational 6 (creating a WSMan session, with the destination name), then connection events for the same shell.
- PowerShell 4103/4104 on the source as well, and
ConsoleHost_history.txtin the user profile if the commands were typed interactively. - 4648 if
-Credentialwas used.
Remote scheduled tasks
schtasks /create /s <host> or the Task Scheduler COM API registers a task on the destination.
- Destination: 4624 type 3, 4698 (task created), 4702 (updated), 4699 (deleted), which require "Other Object Access Events" auditing.
Microsoft-Windows-TaskScheduler/Operational106 (registered), 140 (updated), 141 (deleted), 200/201 (action started/completed). The task XML inC:\Windows\System32\Tasks\and theTaskCacheregistry keys remain after the log rolls. Details on the scheduled tasks page. - Source: Prefetch for
schtasks.exeand a 4648 if credentials were supplied. Attackers often create, run and delete the task within seconds, so look for 4698 and 4699 close together.
DCOM
DCOM lateral movement abuses objects such as MMC20.Application, ShellWindows or ShellBrowserWindow to execute commands remotely over RPC.
- Destination: 4624 type 3, then an unusual process tree: a shell spawned by
mmc.exe(MMC20) or byexplorer.exe(ShellWindows / ShellBrowserWindow), or byDllHost.exefor other objects. Without process creation auditing or EDR, DCOM is hard to prove; Prefetch and memory are your fallbacks. - Source: usually only PowerShell logs or the tool that made the call.
Credential indicators: explicit credentials and pass-the-hash
- 4648 (logon with explicit credentials) is logged on the host where the credentials were used, naming the target account and target server. On a source host it is one of the best records of "this user reached out to that server as that account".
- 4624 type 9 (NewCredentials) with logon process
seclogois whatrunas /netonlyproduces, and also what pass-the-hash tooling produces when it spawns a process with injected credentials. The local identity stays the same; the network identity changes. - On destinations, pass-the-hash appears as 4624 type 3 with NTLM authentication (
AuthenticationPackageName= NTLM) where you would normally expect Kerberos. 4776 (credential validation) is logged on the domain controller for domain accounts, and on the destination itself for local accounts. - Local administrator accounts reused across hosts (same password, same hash) are the usual enabler. A spray of NTLM type 3 logons for
Administratorfrom a single source IP is a pattern worth graphing.
Source to destination reference table
| Technique | Source host | Destination host |
|---|---|---|
| RDP | Terminal Server Client MRU/Servers, Default.rdp, RDPClient 1024/1102, bitmap cache, mstsc.exe Prefetch, 4648 | 4624 type 10/7, 4778/4779, RCM 1149, LSM 21/22/24/25 |
| PsExec | PsExec.exe Prefetch/Amcache, Sysinternals\PsExec key, 4648 | 4624 type 3, 4672, 5145 ADMIN$, 7045 PSEXESVC, PSEXESVC.EXE Prefetch |
| SMB / admin shares | net.exe Prefetch, MountPoints2, ShellBags, 4648 | 4624 type 3, 5140/5145, new files in C:\Windows |
| WMI | wmic.exe Prefetch, PowerShell logs, 4648 | 4624 type 3, children of WmiPrvSE.exe, WMI-Activity |
| WinRM / PS remoting | WinRM 6, PowerShell 4103/4104, ConsoleHost_history.txt, 4648 | 4624 type 3, wsmprovhost.exe, WinRM 91/168, PowerShell 4103/4104, 400/403 |
| Scheduled task | schtasks.exe Prefetch, 4648 | 4624 type 3, 4698/4699/4702, TaskScheduler 106/140/141/200/201, task XML |
| DCOM | PowerShell / tool artifacts | 4624 type 3, shells spawned by mmc.exe, explorer.exe or DllHost.exe |
| Pass-the-hash | 4624 type 9 seclogo | 4624 type 3 NTLM, 4776 (DC or local) |
MITRE ATT&CK catalogues these under Lateral Movement (TA0008), which is useful for mapping findings in a report.
Building the lateral movement graph
A lateral movement investigation is a graph problem: hosts are nodes, authenticated hops are edges. Build it from destination logs first, because they are richer, then confirm edges from the source side.
- Collect Security, System, TerminalServices, WinRM, PowerShell, TaskScheduler and WMI-Activity logs from every host in scope, plus domain controller Security logs. A triage collection with KAPE or Velociraptor is enough; see acquisition and triage.
- Normalise everything to CSV with one parser and one time zone (UTC).
- Extract edges: for each 4624 type 3/10 and each 4648, record time, source IP or workstation, destination host, account and logon ID. Resolve IPs to hostnames with DHCP logs or DNS, since IPs move.
- Filter noise: drop machine accounts, known admin jump hosts and service accounts you have explained, but keep a list of what you dropped.
- Walk from patient zero forward in time, and from each destination backwards to its source. An edge you can see from both ends is strong; an edge seen only on one side is a lead.
# Parse all event logs from a triage collection with EvtxECmd
EvtxECmd.exe -d C:\cases\HOST01\C\Windows\System32\winevt\Logs --csv C:\cases\out --csvf HOST01_evtx.csv
# Quick live check of network and RDP logons on a host
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624} |
Where-Object { $_.Properties[8].Value -in 3,10 } |
Select-Object TimeCreated,
@{n='User';e={$_.Properties[5].Value}},
@{n='LogonType';e={$_.Properties[8].Value}},
@{n='SourceIP';e={$_.Properties[18].Value}}
Then drop the edge list into a spreadsheet pivot, a graph tool, or your super timeline (see building a super timeline with Plaso) so the hops sit next to file and execution evidence.
Tools
- EvtxECmd (Eric Zimmerman): maps normalise the key fields of hundreds of event IDs, which makes the edge extraction above a filter on a CSV.
- Hayabusa: Sigma-based fast triage of EVTX, with built-in rules for PsExec, RDP and WinRM patterns and a logon summary mode.
- Chainsaw: Sigma hunting and keyword/ID search over EVTX, good for sweeping many hosts quickly.
- Velociraptor: run the same hunt across the fleet, for example
Windows.EventLogs.RDPAuthfor RDP logons orWindows.EventLogs.EvtxHunterfor arbitrary IDs, and collect the evidence in one pass.
# Hayabusa: timeline and logon summary for a folder of EVTX files
hayabusa csv-timeline -d ./HOST01/Logs -o HOST01_hayabusa.csv
hayabusa logon-summary -d ./HOST01/Logs
# Chainsaw: Sigma hunt, then a targeted search for service installs
chainsaw hunt ./HOST01/Logs -s ./sigma/rules --mapping ./mappings/sigma-event-logs-all.yml --csv --output ./out
chainsaw search -t 'Event.System.EventID: =7045' ./HOST01/Logs
Command-line flags change between releases of these tools; check --help on the version you run.
When network captures are available, PCAP Parser opens pcap and pcapng files client-side to confirm SMB, RDP, WinRM (5985/5986) and RPC conversations between the hosts on your graph.
Common mistakes
- Treating 1149 as a successful RDP logon. It proves authentication, not a delivered desktop. Confirm with 4624 type 10 and LSM 21/22.
- Hunting on tool names only.
PSEXESVCis trivially renamed with-r, and Impacket uses random names. Hunt on behaviours: service installs pointing toADMIN$, shells underWmiPrvSE.exeorwsmprovhost.exe. - Forgetting the domain controller. 4768/4769 (Kerberos) and 4776 (NTLM) on DCs confirm edges when host logs have rolled.
- Ignoring log retention. The Security log on a busy server can cover only hours. Check the oldest event before concluding that nothing happened.
- Trusting
WorkstationNameoverIpAddress. The workstation name is supplied by the client and can be spoofed or empty. - Assuming missing events mean no activity. Verify the audit policy first, and look for log clearing (1102 / 104), covered in detecting anti-forensics.
Workflow checklist
- Identify the first known compromised host and the time window.
- Collect event logs, registry hives and Prefetch from it and from every host it authenticated to; add DC Security logs.
- Check audit policy and log retention on each host.
- Extract logon edges (4624 types 3/9/10/7, 4648, 4776) and resolve IPs to hosts.
- For each edge, identify the technique with the destination artifacts: 7045 (service), 4698 (task),
WmiPrvSE.exeorwsmprovhost.exechildren, TerminalServices events. - Confirm from the source side: Prefetch, client registry keys, 4648, PowerShell and WinRM logs.
- Build the host graph, then expand scope to every new source or destination you found.
- Correlate with execution and persistence findings (see hunting Windows persistence) and document each edge with its evidence.
Area 08
Lateral Movement
The artifact cheat sheets behind this guide: location by Windows version, what each one proves and the tools to parse it.