Files
mukul975 c21af3347e Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills
- Update all 648 LICENSE files: copyright now reads 'Mahipal'
- Add implementing-security-monitoring-with-datadog (new skill with full anatomy)
- All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
2026-03-11 00:22:12 +01:00

144 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""Agent for detecting C2 beaconing through network traffic frequency analysis."""
import argparse
import json
import math
import os
import re
import sys
from collections import defaultdict
from datetime import datetime, timezone
def parse_zeek_conn_log(log_path):
"""Parse Zeek conn.log and extract connection timestamps per src-dst pair."""
connections = defaultdict(list)
try:
with open(log_path, "r") as f:
for line in f:
if line.startswith("#"):
continue
fields = line.strip().split("\t")
if len(fields) < 7:
continue
ts = float(fields[0])
src, dst = fields[2], fields[4]
dst_port = fields[5]
key = f"{src}->{dst}:{dst_port}"
connections[key].append(ts)
except (FileNotFoundError, ValueError):
pass
return connections
def calculate_jitter(intervals):
"""Calculate jitter (standard deviation of intervals)."""
if len(intervals) < 2:
return 0
mean = sum(intervals) / len(intervals)
variance = sum((x - mean) ** 2 for x in intervals) / len(intervals)
return math.sqrt(variance)
def detect_beaconing(connections, min_connections=10, max_jitter_percent=15):
"""Detect beaconing patterns based on interval regularity."""
beacons = []
for key, timestamps in connections.items():
if len(timestamps) < min_connections:
continue
timestamps.sort()
intervals = [timestamps[i+1] - timestamps[i] for i in range(len(timestamps)-1)]
if not intervals:
continue
mean_interval = sum(intervals) / len(intervals)
if mean_interval == 0:
continue
jitter = calculate_jitter(intervals)
jitter_percent = (jitter / mean_interval) * 100
if jitter_percent <= max_jitter_percent:
parts = key.split("->")
src = parts[0]
dst_port = parts[1] if len(parts) > 1 else ""
beacons.append({
"flow": key,
"connection_count": len(timestamps),
"mean_interval_seconds": round(mean_interval, 2),
"jitter_seconds": round(jitter, 2),
"jitter_percent": round(jitter_percent, 2),
"duration_hours": round((timestamps[-1] - timestamps[0]) / 3600, 2),
"confidence": "HIGH" if jitter_percent < 5 else "MEDIUM",
})
return sorted(beacons, key=lambda x: x["jitter_percent"])
def parse_csv_log(csv_path):
"""Parse generic CSV log with timestamp, src, dst, port columns."""
connections = defaultdict(list)
try:
import csv
with open(csv_path, "r") as f:
reader = csv.DictReader(f)
for row in reader:
ts = row.get("timestamp") or row.get("ts") or row.get("time")
src = row.get("src") or row.get("source") or row.get("src_ip")
dst = row.get("dst") or row.get("destination") or row.get("dst_ip")
port = row.get("dst_port") or row.get("port") or ""
if ts and src and dst:
try:
ts_float = float(ts)
except ValueError:
from datetime import datetime as dt
try:
ts_float = dt.fromisoformat(ts.replace("Z", "+00:00")).timestamp()
except ValueError:
continue
connections[f"{src}->{dst}:{port}"].append(ts_float)
except (FileNotFoundError, KeyError):
pass
return connections
def main():
parser = argparse.ArgumentParser(
description="Detect C2 beaconing via frequency analysis"
)
parser.add_argument("--conn-log", help="Zeek conn.log path")
parser.add_argument("--csv", help="CSV log with timestamp, src, dst columns")
parser.add_argument("--min-connections", type=int, default=10)
parser.add_argument("--max-jitter", type=float, default=15, help="Max jitter percent")
parser.add_argument("--output", "-o", help="Output JSON report")
args = parser.parse_args()
print("[*] Beaconing Detection via Frequency Analysis")
connections = {}
if args.conn_log:
connections = parse_zeek_conn_log(args.conn_log)
elif args.csv:
connections = parse_csv_log(args.csv)
print(f"[*] Unique flows: {len(connections)}")
beacons = detect_beaconing(connections, args.min_connections, args.max_jitter)
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"flows_analyzed": len(connections),
"beacons_detected": len(beacons),
"beacons": beacons[:50],
"risk_level": "CRITICAL" if beacons else "LOW",
}
print(f"[*] Beacons detected: {len(beacons)}")
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[*] Report saved to {args.output}")
else:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()