mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 13:44:56 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
221 lines
8.8 KiB
Python
221 lines
8.8 KiB
Python
#!/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()
|