npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
When to Use
Use this skill when:
- SOC teams need to detect attackers pivoting between systems after initial compromise
- Incident investigations require tracking an attacker's movement path through the network
- Detection engineering needs lateral movement rules mapped to ATT&CK TA0008 techniques
- Red/purple team exercises identify lateral movement detection gaps
Do not use for detecting initial access or external attacks — lateral movement detection focuses on internal host-to-host pivot activity.
Prerequisites
- Windows Security Event Logs (EventCode 4624, 4625, 4648, 4672) from all endpoints and servers
- Sysmon deployed with process creation (EventCode 1), network connections (EventCode 3), and named pipe (EventCode 17/18)
- Network flow data (NetFlow/sFlow, Zeek connection logs) for internal traffic analysis
- SIEM with cross-source correlation capability
- Baseline of normal internal authentication patterns
Workflow
Step 1: Detect Pass-the-Hash / Pass-the-Ticket (T1550)
Pass-the-Hash Detection (EventCode 4624 with NTLM):
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4624 Logon_Type=3
AuthenticationPackageName="NTLM"
| where TargetUserName!="ANONYMOUS LOGON" AND TargetUserName!="$"
| stats count, dc(ComputerName) AS unique_targets, values(ComputerName) AS targets
by src_ip, TargetUserName
| where unique_targets > 3
| eval alert = "Possible Pass-the-Hash: NTLM network logon to ".unique_targets." hosts"
| sort - unique_targets
| table src_ip, TargetUserName, unique_targets, count, targets, alertOverpass-the-Hash Detection (Kerberos with RC4):
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769
TicketEncryptionType="0x17"
| where ServiceName!="krbtgt" AND ServiceName!="$"
| stats count, dc(ServiceName) AS unique_services by src_ip, TargetUserName
| where count > 5
| eval alert = "Possible Overpass-the-Hash: RC4 Kerberos tickets from ".src_ip
| table _time, src_ip, TargetUserName, unique_services, count, alertGolden/Silver Ticket Detection (T1558):
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769
| where TicketOptions="0x40810000" OR TicketOptions="0x40800000"
| eval ticket_lifetime = TicketExpireTime - TicketIssueTime
| where ticket_lifetime > 36000 --- >10 hours (abnormal)
| stats count by src_ip, TargetUserName, ServiceName, TicketEncryptionType, TicketOptions
| eval alert = "Possible Golden/Silver Ticket: Abnormal ticket properties"Step 2: Detect Remote Service Exploitation (T1021)
PsExec Detection (T1021.002):
--- Via Sysmon process creation
index=sysmon EventCode=1
(Image="*\\psexec.exe" OR Image="*\\psexesvc.exe"
OR OriginalFileName="psexec.c" OR OriginalFileName="psexesvc.exe"
OR ParentImage="*\\psexesvc.exe")
| table _time, Computer, User, ParentImage, Image, CommandLine, Hashes
--- Via named pipe creation (Sysmon EventCode 17)
index=sysmon EventCode=17
PipeName IN ("\\PSEXESVC*", "\\RemCom*", "\\csexec*")
| table _time, Computer, User, Image, PipeName
--- Via Windows service creation (EventCode 7045)
index=wineventlog sourcetype="WinEventLog:System" EventCode=7045
ServiceName="PSEXESVC" OR ServiceFileName="*PSEXESVC*"
| table _time, Computer, ServiceName, ServiceFileName, AccountNameWMI Remote Execution (T1047):
index=sysmon EventCode=1
(Image="*\\wmic.exe" AND CommandLine="*/node:*")
OR (ParentImage="*\\WmiPrvSE.exe" AND Image IN ("*\\cmd.exe", "*\\powershell.exe"))
| eval execution_type = case(
match(Image, "wmic"), "WMI Command Line",
match(ParentImage, "WmiPrvSE"), "WMI Provider Host (remote execution)"
)
| table _time, Computer, User, execution_type, ParentImage, Image, CommandLineWinRM/PowerShell Remoting (T1021.006):
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4624
Logon_Type=3 AuthenticationPackageName="Kerberos"
| where ProcessName="*\\wsmprovhost.exe" OR ProcessName="*\\powershell.exe"
| stats count, dc(ComputerName) AS unique_targets by src_ip, TargetUserName
| where unique_targets > 2
| eval alert = "PowerShell Remoting to ".unique_targets." hosts from ".src_ip
--- Sysmon variant
index=sysmon EventCode=1
ParentImage="*\\wsmprovhost.exe"
Image IN ("*\\cmd.exe", "*\\powershell.exe", "*\\csc.exe")
| table _time, Computer, User, Image, CommandLineRDP Lateral Movement (T1021.001):
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4624 Logon_Type=10
| stats count, dc(ComputerName) AS rdp_targets, values(ComputerName) AS destinations,
earliest(_time) AS first_rdp, latest(_time) AS last_rdp
by src_ip, TargetUserName
| where rdp_targets > 2
| eval duration_hours = round((last_rdp - first_rdp) / 3600, 1)
| eval alert = TargetUserName." RDP'd to ".rdp_targets." hosts in ".duration_hours." hours"
| sort - rdp_targetsStep 3: Detect SMB-Based Lateral Movement
Anomalous SMB Traffic Patterns:
index=firewall OR index=zeek sourcetype IN ("pan:traffic", "bro:conn:json")
dest_port=445 action=allowed
| where src_ip!=dest_ip
| stats count AS smb_sessions, dc(dest_ip) AS unique_targets,
sum(bytes_out) AS total_bytes
by src_ip
| where unique_targets > 10
| eval alert = case(
unique_targets > 50, "CRITICAL: Mass SMB enumeration from ".src_ip,
unique_targets > 20, "HIGH: Significant SMB lateral movement",
unique_targets > 10, "MEDIUM: Elevated SMB connections"
)
| sort - unique_targetsAdmin Share Access (C$, ADMIN$):
index=wineventlog sourcetype="WinEventLog:Security" EventCode=5140
ShareName IN ("\\\\*\\C$", "\\\\*\\ADMIN$", "\\\\*\\IPC$")
| where SubjectUserName!="SYSTEM" AND SubjectUserName!="$"
| stats count, dc(ComputerName) AS unique_hosts by SubjectUserName, ShareName, src_ip
| where unique_hosts > 3
| eval alert = "Admin share access to ".unique_hosts." hosts by ".SubjectUserName
| sort - unique_hostsStep 4: Build Lateral Movement Graph
Visualize the attack path:
--- Build source->destination graph for authentication events
index=wineventlog EventCode=4624 Logon_Type IN (3, 10)
earliest=-24h
| stats count AS connections, latest(_time) AS last_connection
by src_ip, ComputerName, TargetUserName, Logon_Type
| eval edge = src_ip." -> ".ComputerName." (User: ".TargetUserName.", Type: ".Logon_Type.")"
| sort - connections
| table edge, connections, last_connection
--- Network flow correlation
index=netflow earliest=-24h
dest_port IN (445, 135, 3389, 5985, 5986)
| stats sum(bytes) AS total_bytes, count AS flow_count,
dc(dest_ip) AS targets by src_ip, dest_port
| where targets > 5
| eval service = case(
dest_port=445, "SMB",
dest_port=135, "RPC/WMI",
dest_port=3389, "RDP",
dest_port IN (5985, 5986), "WinRM"
)
| sort - targets
| table src_ip, service, targets, flow_count, total_bytesStep 5: Detect DCOM and Scheduled Task-Based Movement
DCOM Lateral Execution (T1021.003):
index=sysmon EventCode=1
ParentImage IN ("*\\mmc.exe", "*\\excel.exe", "*\\outlook.exe")
Image IN ("*\\cmd.exe", "*\\powershell.exe", "*\\mshta.exe")
| where ParentCommandLine="*-Embedding*"
| eval alert = "DCOM-based lateral movement: ".ParentImage." spawned ".Image
| table _time, Computer, User, ParentImage, Image, CommandLine, alertRemote Scheduled Task Creation (T1053.005):
index=wineventlog EventCode=4698
| where SubjectUserName!="SYSTEM"
| eval task_xml = TaskContent
| search task_xml="*http*" OR task_xml="*powershell*" OR task_xml="*cmd*" OR task_xml="*\\Temp\\*"
| table _time, Computer, SubjectUserName, TaskName, task_xmlStep 6: Correlate Movement with Kill Chain Phases
Build end-to-end attack chain detection:
--- Detect complete lateral movement sequence
index=wineventlog OR index=sysmon
(EventCode=4625 OR EventCode=4624 OR EventCode=1 OR EventCode=4698 OR EventCode=5140)
| eval phase = case(
EventCode=4625, "1-Recon/BruteForce",
EventCode=4624 AND Logon_Type=3, "2-Lateral Movement",
EventCode=5140 AND match(ShareName, "C\$|ADMIN\$"), "3-Admin Share Access",
EventCode=1 AND match(ParentImage, "psexesvc|WmiPrvSE|wsmprovhost"), "4-Remote Execution",
EventCode=4698, "5-Persistence (Scheduled Task)",
1=1, "other"
)
| where phase!="other"
| stats count by phase, src_ip, ComputerName, TargetUserName
| sort phase, _time
| table phase, src_ip, ComputerName, TargetUserName, countKey Concepts
| Term | Definition |
|---|---|
| Lateral Movement | Post-compromise technique where attackers pivot between systems to reach targets |
| Pass-the-Hash | Using stolen NTLM hash for authentication without knowing the plaintext password |
| Pass-the-Ticket | Using stolen Kerberos TGT/TGS tickets for authentication across the domain |
| PsExec | Sysinternals tool (and attack technique) for remote process execution via SMB and named pipes |
| WMI Execution | Using Windows Management Instrumentation for remote command execution via DCOM or WinRM |
| Admin Share | Default Windows administrative shares (C$, ADMIN$, IPC$) used for remote system management |
Tools & Systems
- Splunk Enterprise Security: SIEM platform for correlating Windows events, Sysmon, and network flows
- Microsoft Defender for Identity: Cloud service detecting lateral movement via domain controller monitoring
- BloodHound: Active Directory attack path analysis tool for identifying lateral movement opportunities
- CrowdStrike Falcon: EDR platform with lateral movement detection and automated containment
- Zeek (Bro): Network monitor generating connection logs for SMB, RDP, and WinRM traffic analysis
Common Scenarios
- PsExec Spread: Attacker uses PsExec to execute malware across 20 workstations — detect via service creation events
- RDP Pivoting: Compromised VPN account used to RDP through multiple internal hosts — detect via Logon_Type 10 chains
- WMI Recon and Execution: Attacker uses WMI for discovery then execution — detect via WmiPrvSE child processes
- Pass-the-Hash Campaign: Stolen local admin hash used across subnet — detect via NTLM Logon_Type 3 to multiple hosts
- Scheduled Task Persistence: Remote scheduled task created on domain controller — detect via EventCode 4698 from non-admin source
Output Format
LATERAL MOVEMENT DETECTION REPORT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Period: 2024-03-15 14:00 to 18:00 UTC
Source: 192.168.1.105 (WORKSTATION-042)
Movement Path:
14:23 192.168.1.105 → 10.0.5.20 (DC-PRIMARY) — PtH via NTLM Type 3
14:25 10.0.5.20 → 10.0.5.21 (DC-BACKUP) — Kerberos ticket reuse
14:28 10.0.5.20 → 10.0.10.15 (FILESERVER-01) — PsExec service creation
14:32 10.0.10.15 → 10.0.10.20 (DB-PRIMARY) — WMI remote execution
14:35 10.0.10.20 → 10.0.10.25 (DB-BACKUP) — SMB admin share access
Techniques Detected:
T1550.002 — Pass-the-Hash (NTLM authentication to DC)
T1021.002 — PsExec (remote service installation)
T1047 — WMI Execution (WmiPrvSE child process)
T1021.002 — SMB Admin Share (C$ access on DB-BACKUP)
Affected Systems: 5 hosts across 2 network segments
User Account: admin_compromised (Domain Admin)
Containment: All 5 hosts isolated at 14:45 UTCReferences and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md1.8 KB
API Reference: Lateral Movement Detection
Windows Event Log IDs
| Event ID | Source | Description |
|---|---|---|
| 4624 | Security | Successful logon (Logon_Type 3=network, 10=RDP) |
| 4625 | Security | Failed logon attempt |
| 4648 | Security | Explicit credential logon (runas) |
| 4672 | Security | Special privileges assigned (admin logon) |
| 4769 | Security | Kerberos TGS request (Pass-the-Ticket) |
| 5140 | Security | Network share access (C$, ADMIN$, IPC$) |
| 7045 | System | New service installed (PsExec) |
Sysmon Event Codes
| Event Code | Description |
|---|---|
| 1 | Process creation with command line |
| 3 | Network connection |
| 10 | Process access (LSASS credential dumping) |
| 17/18 | Named pipe created/connected (PsExec) |
MITRE ATT&CK Techniques (TA0008)
| Technique | ID | Detection Signal |
|---|---|---|
| Pass-the-Hash | T1550.002 | NTLM Type 3 logon to multiple hosts |
| PsExec | T1021.002 | PSEXESVC service creation + named pipe |
| WMI Execution | T1047 | WmiPrvSE spawning cmd/powershell |
| RDP | T1021.001 | Logon_Type 10 to multiple targets |
| SMB Admin Share | T1021.002 | EventCode 5140 on C$/ADMIN$ |
Python Libraries
| Library | Version | Purpose |
|---|---|---|
csv |
stdlib | Parse exported Windows event logs |
json |
stdlib | Report output generation |
collections |
stdlib | Event aggregation and counting |
References
- MITRE ATT&CK Lateral Movement: https://attack.mitre.org/tactics/TA0008/
- Splunk Security Essentials: https://splunkbase.splunk.com/app/3435
- Sigma rules (lateral movement): https://github.com/SigmaHQ/sigma
- Microsoft Defender for Identity: https://learn.microsoft.com/en-us/defender-for-identity/
Scripts 1
agent.py8.8 KB
#!/usr/bin/env python3
"""Agent for performing lateral movement detection.
Analyzes Windows event logs and network flow data to detect
Pass-the-Hash, PsExec, WMI, RDP, and SMB-based lateral movement
mapped to MITRE ATT&CK TA0008 techniques.
"""
import json
import sys
import csv
from collections import defaultdict
from datetime import datetime
from pathlib import Path
class LateralMovementDetector:
"""Detects lateral movement patterns from log data."""
def __init__(self, output_dir):
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.auth_events = []
self.process_events = []
self.network_flows = []
def load_auth_events(self, csv_path):
"""Load Windows authentication events (4624, 4625, 4648, 4672)."""
with open(csv_path, "r") as f:
for row in csv.DictReader(f):
self.auth_events.append(row)
def load_process_events(self, csv_path):
"""Load Sysmon process creation events (EventCode 1)."""
with open(csv_path, "r") as f:
for row in csv.DictReader(f):
self.process_events.append(row)
def load_network_flows(self, csv_path):
"""Load network flow data (NetFlow/Zeek)."""
with open(csv_path, "r") as f:
for row in csv.DictReader(f):
self.network_flows.append(row)
def detect_pass_the_hash(self):
"""Detect Pass-the-Hash via NTLM Type 3 logons to multiple hosts."""
ntlm_logons = defaultdict(lambda: {"targets": set(), "count": 0, "events": []})
for event in self.auth_events:
if (event.get("EventCode") == "4624" and
event.get("Logon_Type") == "3" and
event.get("AuthenticationPackageName", "").upper() == "NTLM"):
user = event.get("TargetUserName", "")
src = event.get("src_ip", event.get("IpAddress", ""))
target = event.get("ComputerName", "")
if user and not user.endswith("$") and user != "ANONYMOUS LOGON":
key = f"{src}|{user}"
ntlm_logons[key]["targets"].add(target)
ntlm_logons[key]["count"] += 1
ntlm_logons[key]["events"].append(event)
findings = []
for key, data in ntlm_logons.items():
if len(data["targets"]) > 3:
src_ip, user = key.split("|", 1)
findings.append({
"technique": "T1550.002",
"name": "Pass-the-Hash",
"src_ip": src_ip,
"user": user,
"unique_targets": len(data["targets"]),
"total_logons": data["count"],
"targets": sorted(data["targets"]),
})
return findings
def detect_psexec(self):
"""Detect PsExec execution via process creation and service events."""
findings = []
for event in self.process_events:
image = event.get("Image", "").lower()
parent = event.get("ParentImage", "").lower()
if "psexec" in image or "psexesvc" in image or "psexesvc" in parent:
findings.append({
"technique": "T1021.002",
"name": "PsExec Execution",
"computer": event.get("Computer", ""),
"user": event.get("User", ""),
"image": event.get("Image", ""),
"parent": event.get("ParentImage", ""),
"cmdline": event.get("CommandLine", ""),
"timestamp": event.get("timestamp", event.get("UtcTime", "")),
})
return findings
def detect_wmi_execution(self):
"""Detect WMI remote execution via WmiPrvSE child processes."""
findings = []
for event in self.process_events:
parent = event.get("ParentImage", "").lower()
image = event.get("Image", "").lower()
if "wmiprvse" in parent and ("cmd.exe" in image or "powershell" in image):
findings.append({
"technique": "T1047",
"name": "WMI Remote Execution",
"computer": event.get("Computer", ""),
"user": event.get("User", ""),
"image": event.get("Image", ""),
"cmdline": event.get("CommandLine", ""),
"timestamp": event.get("timestamp", ""),
})
return findings
def detect_rdp_lateral(self):
"""Detect RDP lateral movement via Logon_Type 10."""
rdp_sessions = defaultdict(lambda: {"targets": set(), "count": 0})
for event in self.auth_events:
if event.get("EventCode") == "4624" and event.get("Logon_Type") == "10":
src = event.get("src_ip", event.get("IpAddress", ""))
user = event.get("TargetUserName", "")
target = event.get("ComputerName", "")
key = f"{src}|{user}"
rdp_sessions[key]["targets"].add(target)
rdp_sessions[key]["count"] += 1
findings = []
for key, data in rdp_sessions.items():
if len(data["targets"]) > 2:
src_ip, user = key.split("|", 1)
findings.append({
"technique": "T1021.001",
"name": "RDP Lateral Movement",
"src_ip": src_ip,
"user": user,
"unique_targets": len(data["targets"]),
"targets": sorted(data["targets"]),
})
return findings
def detect_smb_scanning(self):
"""Detect mass SMB connections indicating lateral movement."""
smb_sources = defaultdict(lambda: {"targets": set(), "bytes": 0})
for flow in self.network_flows:
if flow.get("dest_port") == "445":
src = flow.get("src_ip", "")
dst = flow.get("dest_ip", "")
smb_sources[src]["targets"].add(dst)
smb_sources[src]["bytes"] += int(flow.get("bytes", 0))
findings = []
for src, data in smb_sources.items():
if len(data["targets"]) > 10:
findings.append({
"technique": "T1021.002",
"name": "SMB Mass Connection",
"src_ip": src,
"unique_targets": len(data["targets"]),
"total_bytes": data["bytes"],
"severity": "CRITICAL" if len(data["targets"]) > 50 else "HIGH",
})
return findings
def build_movement_graph(self):
"""Build a source->destination graph of lateral movement."""
edges = defaultdict(int)
for event in self.auth_events:
if event.get("EventCode") == "4624" and event.get("Logon_Type") in ("3", "10"):
src = event.get("src_ip", event.get("IpAddress", ""))
dst = event.get("ComputerName", "")
user = event.get("TargetUserName", "")
if src and dst:
edges[f"{src} -> {dst} ({user})"] += 1
return dict(sorted(edges.items(), key=lambda x: x[1], reverse=True)[:50])
def generate_report(self):
"""Run all detections and generate a comprehensive report."""
report = {
"report_date": datetime.utcnow().isoformat(),
"detections": {
"pass_the_hash": self.detect_pass_the_hash(),
"psexec": self.detect_psexec(),
"wmi_execution": self.detect_wmi_execution(),
"rdp_lateral": self.detect_rdp_lateral(),
"smb_scanning": self.detect_smb_scanning(),
},
"movement_graph": self.build_movement_graph(),
}
total_findings = sum(len(v) for v in report["detections"].values())
report["summary"] = {
"total_findings": total_findings,
"techniques_detected": [k for k, v in report["detections"].items() if v],
}
report_path = self.output_dir / "lateral_movement_report.json"
with open(report_path, "w") as f:
json.dump(report, f, indent=2, default=list)
print(json.dumps(report, indent=2, default=list))
return report
def main():
if len(sys.argv) < 3:
print("Usage: agent.py <auth_events_csv> <output_dir> [process_csv] [flows_csv]")
sys.exit(1)
auth_csv = sys.argv[1]
output_dir = sys.argv[2]
detector = LateralMovementDetector(output_dir)
detector.load_auth_events(auth_csv)
if len(sys.argv) > 3:
detector.load_process_events(sys.argv[3])
if len(sys.argv) > 4:
detector.load_network_flows(sys.argv[4])
detector.generate_report()
if __name__ == "__main__":
main()