Lateral Movement Traffic
Lateral movement is how attackers spread from their initial beachhead to their target systems. Every technique leaves distinctive traffic patterns because Windows authentication protocols and remote execution mechanisms each have unique packet signatures. This chapter maps each major lateral movement technique to its network fingerprint, the display filters to isolate it in Wireshark, and the Zeek/Suricata detections that surface it.
During a ransomware incident, the IR team needs to trace which hosts the ransomware actor visited after initially compromising a workstation. They pull Zeek conn.log for the internal network for 48 hours before encryption began. By querying for connections on ports 445, 135, 3389, 5985, and 88 from the initial compromised host, they reconstruct the exact path the attacker took through the network — and find two hosts they didn't know were compromised.
Technique → Traffic Fingerprint
| Technique | Port(s) | Protocol | Key Indicator | Wireshark Filter |
|---|---|---|---|---|
| PsExec | 445 | SMB2 | ADMIN$ + svcctl named pipe + service creation | smb2 && smb2.filename == "svcctl" |
| WMI | 135 + dynamic | DCERPC | port 135 + high ephemeral port on same pair | dcerpc && dcerpc.opnum == 3 |
| WinRM / PSRemoting | 5985 (HTTP) / 5986 (HTTPS) | WS-Man | HTTP POST /wsman with SOAP body | tcp.port == 5985 && http.request.method == "POST" |
| RDP | 3389 | RDP | X.224 Connection Request with username cookie | rdp && rdp.rdpNegReq |
| AT/Scheduled Task | 445 | SMB2 | atsvc named pipe via IPC$ | smb2.filename == "atsvc" |
| DCOM | 135 + dynamic | DCERPC | IRemoteActivation RPC call | dcerpc.opnum == 4 |
| SMB admin share | 445 | SMB2 | Tree Connect to \\host\ADMIN$ or \\host\C$ | smb2.tree contains "ADMIN$" |
| Pass-the-Hash | 445 | SMB/NTLM | NTLM auth without prior Kerberos attempt | ntlmssp |
| Pass-the-Ticket | 88, 445 | Kerberos | AP-REQ with ticket from different hostname | kerberos |
| Over-PTH (WinRM) | 5985 | HTTP/NTLM | NTLM challenge/response in WS-Man headers | tcp.port==5985 && ntlmssp |
Traffic Analysis
#!/bin/bash
PCAP="${1:-capture.pcap}"
SUSPECT_HOST="${2:-10.0.0.50}"
echo "=== PsExec pattern: ADMIN$ + svcctl ==="
tshark -r "$PCAP" -Y "smb2 and ip.addr==$SUSPECT_HOST" \
-T fields -e frame.time -e ip.src -e ip.dst \
-e smb2.filename -e smb2.cmd | \
grep -E "(ADMIN\$|IPC\$|svcctl|atsvc)" | head -20
echo ""
echo "=== NTLM authentication (pass-the-hash candidate) ==="
tshark -r "$PCAP" -Y "ntlmssp and ip.src==$SUSPECT_HOST" \
-T fields -e frame.time -e ip.src -e ip.dst \
-e ntlmssp.messagetype -e ntlmssp.auth.username \
-e ntlmssp.auth.domain 2>/dev/null | head -20
echo ""
echo "=== WinRM connections (PowerShell remoting) ==="
tshark -r "$PCAP" -Y "tcp.port==5985 or tcp.port==5986" \
-T fields -e frame.time -e ip.src -e ip.dst \
-e http.request.method -e http.request.uri \
-e http.response.code 2>/dev/null | head -20
echo ""
echo "=== Kerberos TGS requests (service ticket requests) ==="
tshark -r "$PCAP" -Y "kerberos.msg_type==12 and ip.src==$SUSPECT_HOST" \
-T fields -e frame.time -e ip.src -e ip.dst \
-e kerberos.req.sname \
2>/dev/null | head -20
echo ""
echo "=== All internal connections from suspect host ==="
tshark -r "$PCAP" \
-Y "ip.src==$SUSPECT_HOST and ip.dst[0:2] == 10:00" \
-T fields -e frame.time -e ip.dst -e tcp.dstport \
2>/dev/null | sort -k3 -n | sort -k2 -u | head -30
echo ""
echo "=== Timeline: when did connections to each unique internal host begin? ==="
tshark -r "$PCAP" -Y "ip.src==$SUSPECT_HOST" \
-T fields -e frame.time_relative -e ip.dst -e tcp.dstport \
2>/dev/null | sort -k2 | head -30
An attacker moving laterally through a network creates a directed graph: compromised_host_A → new_host_B → new_host_C. Each edge in this graph is a network connection (SMB, RDP, WMI, or WinRM). The timestamp of each connection gives you the attacker's traversal timeline. When investigating lateral movement, your goal is to reconstruct this graph from network data. Start with the first known compromised host and find all the internal hosts it connected to on lateral movement ports (445, 3389, 5985, 135, 88). Then, for each of those destination hosts, find what internal hosts THEY connected to in the period after the initial contact — these are second-generation pivots. Continue recursively. In practice: Zeek conn.log + Python networkx graph library makes this trivially automatable. Query conn.log for all internal-to-internal connections on lateral movement ports, build a directed graph, find the connected component containing the initial compromised host, and you have the full picture of the attacker's movement.
Q & A
Q: How do I distinguish legitimate PsExec usage by sysadmins from attacker PsExec in network captures?
Network traffic alone cannot reliably distinguish legitimate from malicious PsExec — both create identical SMB2 + svcctl named pipe patterns. Differentiation requires context: (1) Source host: legitimate sysadmin use originates from IT management workstations or jump hosts. Attacker use originates from compromised end-user workstations. Build a baseline of which hosts legitimately run PsExec and alert when new source hosts are observed. (2) Time of day: legitimate IT operations happen during business hours. 2 AM PsExec from a user workstation is always suspicious. (3) Target scope: a single admin running PsExec to a handful of servers during a patching window looks different from an attacker connecting to 50 unique hosts in 15 minutes. Use the fan-out threshold from the lateral movement detection chapter. (4) Authentication type: legitimate PsExec normally uses Kerberos. Attacker Pass-the-Hash uses NTLM. NTLM-authenticated SMB connections (ntlmssp in the capture) to unusual destinations from unusual source hosts are the clearest distinguishing signal. (5) Endpoint correlation: check EDR/Sysmon logs for process creation of PSEXESVC.exe or service installations on the destination host — legitimate usage will usually have a corresponding service creation event that maps to the network event.