Files
Anthropic-Cybersecurity-Skills/skills/analyzing-network-traffic-for-incidents/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- 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
2026-03-19 13:26:49 +01:00

206 lines
7.6 KiB
Python

#!/usr/bin/env python3
"""Network traffic incident analysis agent using scapy and tshark for PCAP investigation."""
import subprocess
import os
import sys
import json
import statistics
from collections import defaultdict
try:
from scapy.all import rdpcap, IP, TCP, DNS
HAS_SCAPY = True
except ImportError:
HAS_SCAPY = False
def run_tshark(pcap_path, display_filter, fields):
"""Run tshark with a display filter and extract specific fields."""
cmd = ["tshark", "-r", pcap_path, "-Y", display_filter, "-T", "fields"]
for f in fields:
cmd += ["-e", f]
cmd += ["-E", "separator=|"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
rows = []
if result.returncode == 0:
for line in result.stdout.strip().splitlines():
parts = line.split("|")
if len(parts) == len(fields):
rows.append(dict(zip(fields, parts)))
return rows
def get_pcap_summary(pcap_path):
"""Get high-level PCAP statistics."""
cmd = ["tshark", "-r", pcap_path, "-q", "-z", "conv,ip"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
return result.stdout if result.returncode == 0 else ""
def detect_lateral_movement(pcap_path):
"""Detect potential lateral movement patterns (SMB, RDP, WinRM, SSH)."""
lateral_ports = {"445": "SMB", "3389": "RDP", "5985": "WinRM", "5986": "WinRM-S",
"22": "SSH", "135": "RPC", "139": "NetBIOS"}
connections = run_tshark(pcap_path, "tcp.flags.syn==1 && tcp.flags.ack==0",
["ip.src", "ip.dst", "tcp.dstport"])
lateral = []
for conn in connections:
port = conn.get("tcp.dstport", "")
if port in lateral_ports:
lateral.append({
"src": conn["ip.src"],
"dst": conn["ip.dst"],
"port": port,
"service": lateral_ports[port],
})
return lateral
def detect_data_exfiltration(pcap_path, threshold_mb=10):
"""Detect potential data exfiltration based on outbound data volume."""
cmd = ["tshark", "-r", pcap_path, "-q", "-z", "conv,ip"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
suspects = []
if result.returncode == 0:
for line in result.stdout.splitlines():
parts = line.split()
if len(parts) >= 8 and "<->" in line:
try:
ip_a = parts[0]
ip_b = parts[2]
bytes_a_to_b = int(parts[4]) if parts[4].isdigit() else 0
bytes_b_to_a = int(parts[7]) if len(parts) > 7 and parts[7].isdigit() else 0
total_bytes = bytes_a_to_b + bytes_b_to_a
if total_bytes > threshold_mb * 1024 * 1024:
suspects.append({
"ip_a": ip_a,
"ip_b": ip_b,
"bytes_a_to_b": bytes_a_to_b,
"bytes_b_to_a": bytes_b_to_a,
"total_mb": round(total_bytes / (1024 * 1024), 2),
})
except (ValueError, IndexError):
continue
return suspects
def detect_beaconing(pcap_path, min_conns=10):
"""Detect periodic beaconing patterns from TCP connections."""
if not HAS_SCAPY:
return []
packets = rdpcap(pcap_path)
conn_times = defaultdict(list)
for pkt in packets:
if IP in pkt and TCP in pkt and (pkt[TCP].flags & 0x02):
key = f"{pkt[IP].src}->{pkt[IP].dst}:{pkt[TCP].dport}"
conn_times[key].append(float(pkt.time))
beacons = []
for key, times in conn_times.items():
if len(times) < min_conns:
continue
intervals = [times[i+1] - times[i] for i in range(len(times)-1)]
avg = statistics.mean(intervals)
std = statistics.stdev(intervals) if len(intervals) > 1 else 0
jitter = (std / avg * 100) if avg > 0 else 0
if 5 < avg < 7200 and jitter < 30:
beacons.append({
"flow": key,
"connections": len(times),
"avg_interval": round(avg, 1),
"jitter_pct": round(jitter, 1),
})
return beacons
def extract_dns_queries(pcap_path):
"""Extract DNS queries and identify suspicious patterns."""
queries = run_tshark(pcap_path, "dns.qr==0",
["ip.src", "dns.qry.name", "dns.qry.type"])
return queries
def detect_ids_alerts(pcap_path):
"""Run Suricata on the PCAP and extract alerts."""
import tempfile
suricata_output = os.environ.get("SURICATA_OUTPUT_DIR", os.path.join(tempfile.gettempdir(), "suricata_output"))
os.makedirs(suricata_output, exist_ok=True)
cmd = ["suricata", "-r", pcap_path, "-l", suricata_output, "-k", "none"]
subprocess.run(cmd, capture_output=True, timeout=120)
alerts = []
alert_file = os.path.join(suricata_output, "fast.log")
if os.path.exists(alert_file):
with open(alert_file, "r") as f:
for line in f:
alerts.append(line.strip())
return alerts
def extract_http_objects(pcap_path, output_dir):
"""Extract HTTP objects (files) from the PCAP."""
os.makedirs(output_dir, exist_ok=True)
cmd = ["tshark", "-r", pcap_path, "--export-objects", f"http,{output_dir}"]
subprocess.run(cmd, capture_output=True, timeout=60)
exported = []
if os.path.exists(output_dir):
for f in os.listdir(output_dir):
filepath = os.path.join(output_dir, f)
exported.append({"filename": f, "size": os.path.getsize(filepath)})
return exported
def generate_incident_report(pcap_path, beacons, lateral, exfil, dns_queries):
"""Generate a network incident analysis report."""
report = {
"pcap": pcap_path,
"pcap_size_mb": round(os.path.getsize(pcap_path) / (1024*1024), 1),
"findings": {
"beacons_detected": len(beacons),
"lateral_movement_flows": len(lateral),
"exfiltration_suspects": len(exfil),
"dns_queries": len(dns_queries),
},
"beacons": beacons,
"lateral_movement": lateral[:10],
"exfiltration": exfil,
}
return report
if __name__ == "__main__":
print("=" * 60)
print("Network Traffic Incident Analysis Agent")
print("Beaconing, lateral movement, exfiltration detection")
print("=" * 60)
pcap = sys.argv[1] if len(sys.argv) > 1 else None
if pcap and os.path.exists(pcap):
print(f"\n[*] Analyzing: {pcap}")
print(f"[*] Size: {os.path.getsize(pcap)/(1024*1024):.1f} MB")
print("\n--- Beacon Detection ---")
beacons = detect_beaconing(pcap)
for b in beacons:
print(f" [!] {b['flow']}: interval={b['avg_interval']}s "
f"jitter={b['jitter_pct']}% ({b['connections']} conns)")
print("\n--- Lateral Movement Detection ---")
lateral = detect_lateral_movement(pcap)
for l in lateral[:10]:
print(f" [!] {l['src']} -> {l['dst']}:{l['port']} ({l['service']})")
print("\n--- Data Exfiltration Detection ---")
exfil = detect_data_exfiltration(pcap, threshold_mb=5)
for e in exfil:
print(f" [!] {e['ip_a']} <-> {e['ip_b']}: {e['total_mb']} MB")
print("\n--- DNS Queries ---")
dns = extract_dns_queries(pcap)
print(f" Total queries: {len(dns)}")
report = generate_incident_report(pcap, beacons, lateral, exfil, dns)
print(f"\n[*] Report summary: {json.dumps(report['findings'], indent=2)}")
else:
print(f"\n[DEMO] Usage: python agent.py <capture.pcap>")