Chapter 59

Lateral Movement in Flow Data

Lateral movement — an attacker pivoting from one compromised host to others on the internal network — creates distinctive flow patterns: a single source connecting to many internal destinations on SMB, WMI, RDP, or Kerberos ports in a short time window. NetFlow's internal-to-internal traffic visibility is particularly valuable because packet sensors are often placed only at the perimeter, missing east-west traffic entirely.

Scenario

An analyst reviews NetFlow for the 24 hours after a phishing compromise. The initially compromised host (10.0.1.50) shows normal internet browsing. But NetFlow from the internal core switch reveals that 10.0.1.50 made TCP connections to 47 internal hosts on port 445 (SMB) between 02:15 and 02:30 AM — a 15-minute window of systematic SMB scanning that predates any endpoint detection alert. NetFlow provided 6 hours of lead time over the EDR alert.

Lateral Movement Flow Patterns

  Lateral Movement Techniques → Expected NetFlow Signatures
  ═══════════════════════════════════════════════════════════════════

  PsExec / SMB-based:
  └── src_ip → many dst_ips, dst_port=445
      Indicator: 1 source, N>10 unique destinations, port 445
      Pattern: fan-out in short window (minutes)

  WMI lateral movement:
  └── src_ip → dst_ip, dst_port=135 then high port (dynamic RPC)
      Indicator: port 135 followed by ephemeral port to same dst
      Pattern: pairs of (135, high_port) flows

  RDP brute force / spreading:
  └── src_ip → many dst_ips, dst_port=3389
      Indicator: fan-out on port 3389

  Pass-the-Hash / Pass-the-Ticket:
  └── src_ip → dst_ip, dst_port=445 or 88
      Indicator: authentication protocols suddenly from unusual source
      Hard to distinguish from legitimate without protocol detail

  Kerberoasting (scan phase):
  └── src_ip → DC_ip, dst_port=88
      Indicator: many short flows to DC port 88 from single source

  Internal port scan (reconnaissance):
  └── src_ip → sequential dst_ips, various dst_ports
      Indicator: high unique-dst-ip count in short window, S0 conn_state

  Host-to-host data staging:
  └── src_ip → dst_ip, large bytes_toserver
      Indicator: large east-west transfer to unusual destination

Lateral Movement Detection

bashlateral-movement-netflow.sh
#!/bin/bash
FLOW_DIR="${1:-/data/netflow}"

echo "=== SMB fan-out: sources connecting to many internal SMB targets ==="
# Find hosts connecting to >10 unique internal IPs on port 445 in the dataset
nfdump -R "$FLOW_DIR" -q \
    -f "dst port 445 and src net 10.0.0.0/8 and dst net 10.0.0.0/8" \
    -o "fmt:%sa %da" | \
    awk '{
        seen[$1][$2] = 1
    }
    END {
        for (src in seen) {
            count = 0
            for (dst in seen[src]) count++
            if (count > 10)
                printf "SMB_fan_out: %s → %d unique targets\n", src, count
        }
    }' | sort -t':' -k2 -rn | head -20

echo ""
echo "=== RDP fan-out: same query for port 3389 ==="
nfdump -R "$FLOW_DIR" -q \
    -f "dst port 3389 and src net 10.0.0.0/8 and dst net 10.0.0.0/8" \
    -o "fmt:%sa %da" | \
    awk '{seen[$1][$2]=1} END {
        for(src in seen) {
            n=0; for(d in seen[src]) n++
            if(n>5) printf "RDP_fan_out: %s → %d\n", src, n
        }
    }' | sort -t':' -k2 -rn

echo ""
echo "=== Large internal transfers (data staging) ==="
nfdump -R "$FLOW_DIR" -q \
    -f "src net 10.0.0.0/8 and dst net 10.0.0.0/8" \
    -s record/bytes -n 20 \
    -o "fmt:%sa %da %sp %dp %byt"

echo ""
echo "=== Sources connecting to DC on Kerberos port 88 ==="
nfdump -R "$FLOW_DIR" -q \
    -f "dst port 88 and src net 10.0.0.0/8" \
    -o "fmt:%sa %da" | \
    awk '{count[$1]++} END {for(src in count) if(count[src]>5) print count[src], src}' | \
    sort -rn | head -10

echo ""
echo "=== Port 135 + ephemeral port pairs (WMI indicator) ==="
nfdump -R "$FLOW_DIR" -q \
    -f "dst port 135 and src net 10.0.0.0/8" \
    -o "fmt:%ts %sa %da" | \
    awk '{
        wmi_ts[$2"_"$3] = $1
        wmi_src[$2"_"$3] = $2
        wmi_dst[$2"_"$3] = $3
    }
    END {
        for(key in wmi_ts)
            print "WMI_initiate:", wmi_ts[key], key
    }' | head -10
Why east-west NetFlow is more valuable than perimeter PCAP for lateral movement

Perimeter packet sensors only see traffic that crosses the internet boundary. Lateral movement, by definition, happens entirely inside the network: compromised host → domain controller, then domain controller → file server, then file server → workstation where the target data lives. None of this traffic ever hits the perimeter sensor. NetFlow from the internal core switch, by contrast, sees all east-west traffic regardless of whether it goes to the internet. In an enterprise with a well-segmented network and NetFlow on the core switch, every lateral movement attempt is visible in the flow data. The challenge is: at 1000 internal hosts, a core switch exports tens of millions of flow records per day. Efficient detection requires focused queries (port-based filtering, time-windowed counting, statistical anomalies) rather than raw inspection of all flow records. The queries in this chapter are designed to surface lateral movement from millions of flow records in seconds.

Q & A

Q: I'm seeing SMB fan-out alerts but they're all from our vulnerability scanner. How do I exclude it?

Allowlist your scanner in the query, not the data: add and not src ip 10.0.0.5 to the nfdump filter (replacing 10.0.0.5 with the scanner IP). For a Python or SQL-based approach: maintain an allowlist table of known scanners, monitoring tools, and backup agents, and filter them from the beaconing and lateral movement detection queries. Important: do NOT hard-code the allowlist in the raw flow collection pipeline or you'll lose data you may need for an incident investigation later (e.g., if the scanner itself is compromised and used to pivot). Always filter in the detection layer, not the collection layer — keep complete raw data and apply exclusions at query time. A useful approach: alert on scanner activity but at a lower priority than other SMB fan-out sources. If the scanner suddenly fans out 10× more than normal (e.g., because an attacker is using it as a proxy), you still want to know — the anomaly is relative to the scanner's baseline, not an absolute threshold.