mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 21:54:56 +03:00
27c6414ca5
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
169 lines
6.6 KiB
Python
169 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Container escape detection agent using Falco output parsing and audit log analysis.
|
|
|
|
Monitors for container escape indicators by parsing Falco JSON alerts,
|
|
auditd logs, and Docker inspect data for privileged/vulnerable containers.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
ESCAPE_VECTORS = {
|
|
"nsenter": {"severity": "CRITICAL", "mitre": "T1611", "desc": "Namespace escape via nsenter"},
|
|
"unshare": {"severity": "CRITICAL", "mitre": "T1611", "desc": "Namespace manipulation"},
|
|
"mount": {"severity": "HIGH", "mitre": "T1611", "desc": "Host filesystem mount"},
|
|
"modprobe": {"severity": "CRITICAL", "mitre": "T1611", "desc": "Kernel module loading"},
|
|
"insmod": {"severity": "CRITICAL", "mitre": "T1611", "desc": "Kernel module insertion"},
|
|
"chroot": {"severity": "HIGH", "mitre": "T1611", "desc": "Chroot escape attempt"},
|
|
}
|
|
|
|
SENSITIVE_PATHS = [
|
|
"/var/run/docker.sock", "/proc/sysrq-trigger", "/proc/kcore",
|
|
"/proc/kmsg", "/proc/kallsyms", "/sys/kernel",
|
|
"/etc/shadow", "/etc/kubernetes/admin.conf",
|
|
]
|
|
|
|
|
|
def parse_falco_json(filepath):
|
|
alerts = []
|
|
with open(filepath, "r") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
evt = json.loads(line)
|
|
if any(tag in evt.get("tags", []) for tag in ["escape", "container"]):
|
|
alerts.append({
|
|
"time": evt.get("time", ""),
|
|
"rule": evt.get("rule", ""),
|
|
"priority": evt.get("priority", ""),
|
|
"output": evt.get("output", ""),
|
|
"output_fields": evt.get("output_fields", {}),
|
|
})
|
|
except json.JSONDecodeError:
|
|
continue
|
|
return alerts
|
|
|
|
|
|
def parse_auditd_escape_events(filepath):
|
|
findings = []
|
|
escape_keys = {"container_escape", "container_mount", "kernel_module",
|
|
"docker_socket", "process_trace"}
|
|
with open(filepath, "r") as f:
|
|
for line in f:
|
|
for key in escape_keys:
|
|
if f'key="{key}"' in line or f"key={key}" in line:
|
|
timestamp = re.search(r'msg=audit\((\d+\.\d+):', line)
|
|
syscall = re.search(r'syscall=(\w+)', line)
|
|
exe = re.search(r'exe="([^"]+)"', line)
|
|
findings.append({
|
|
"timestamp": timestamp.group(1) if timestamp else "",
|
|
"key": key,
|
|
"syscall": syscall.group(1) if syscall else "",
|
|
"exe": exe.group(1) if exe else "",
|
|
"severity": "CRITICAL",
|
|
"raw": line.strip()[:200],
|
|
})
|
|
return findings
|
|
|
|
|
|
def check_privileged_containers():
|
|
containers = []
|
|
try:
|
|
result = subprocess.run(
|
|
["docker", "ps", "--format", "{{.ID}} {{.Names}} {{.Image}}"],
|
|
capture_output=True, text=True, timeout=10)
|
|
if result.returncode != 0:
|
|
return containers
|
|
for line in result.stdout.strip().split("\n"):
|
|
if not line.strip():
|
|
continue
|
|
parts = line.split(None, 2)
|
|
cid = parts[0]
|
|
inspect = subprocess.run(
|
|
["docker", "inspect", "--format",
|
|
"{{.HostConfig.Privileged}} {{.HostConfig.PidMode}} "
|
|
"{{range .HostConfig.Binds}}{{.}} {{end}}"],
|
|
capture_output=True, text=True, timeout=10)
|
|
if inspect.returncode == 0:
|
|
info = inspect.stdout.strip()
|
|
findings = []
|
|
if "true" in info.split()[0:1]:
|
|
findings.append("privileged_mode")
|
|
if "host" in info:
|
|
findings.append("host_pid_namespace")
|
|
if "/var/run/docker.sock" in info:
|
|
findings.append("docker_socket_mounted")
|
|
if findings:
|
|
containers.append({
|
|
"container_id": cid,
|
|
"name": parts[1] if len(parts) > 1 else "",
|
|
"image": parts[2] if len(parts) > 2 else "",
|
|
"escape_risks": findings,
|
|
"severity": "CRITICAL" if "privileged_mode" in findings else "HIGH",
|
|
})
|
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
pass
|
|
return containers
|
|
|
|
|
|
def check_dangerous_capabilities(container_id):
|
|
dangerous_caps = {"SYS_ADMIN", "SYS_PTRACE", "NET_ADMIN", "SYS_RAWIO",
|
|
"SYS_MODULE", "DAC_READ_SEARCH"}
|
|
try:
|
|
result = subprocess.run(
|
|
["docker", "inspect", "--format", "{{.HostConfig.CapAdd}}", container_id],
|
|
capture_output=True, text=True, timeout=10)
|
|
if result.returncode == 0:
|
|
caps = set(re.findall(r'\b([A-Z_]+)\b', result.stdout))
|
|
found = caps & dangerous_caps
|
|
return [{"capability": c, "severity": "CRITICAL"} for c in found]
|
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
pass
|
|
return []
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Container Escape Detector")
|
|
parser.add_argument("--falco-log", help="Path to Falco JSON output log")
|
|
parser.add_argument("--audit-log", help="Path to auditd log file")
|
|
parser.add_argument("--check-containers", action="store_true",
|
|
help="Check running containers for escape risks")
|
|
parser.add_argument("--container-id", help="Check specific container capabilities")
|
|
args = parser.parse_args()
|
|
|
|
results = {"timestamp": datetime.utcnow().isoformat() + "Z", "findings": []}
|
|
|
|
if args.falco_log:
|
|
alerts = parse_falco_json(args.falco_log)
|
|
results["falco_alerts"] = alerts
|
|
results["findings"].extend([{"source": "falco", **a} for a in alerts])
|
|
|
|
if args.audit_log:
|
|
audit = parse_auditd_escape_events(args.audit_log)
|
|
results["audit_events"] = audit
|
|
results["findings"].extend([{"source": "auditd", **a} for a in audit])
|
|
|
|
if args.check_containers:
|
|
priv = check_privileged_containers()
|
|
results["privileged_containers"] = priv
|
|
results["findings"].extend([{"source": "docker_inspect", **c} for c in priv])
|
|
|
|
if args.container_id:
|
|
caps = check_dangerous_capabilities(args.container_id)
|
|
results["dangerous_capabilities"] = caps
|
|
results["findings"].extend([{"source": "capabilities", **c} for c in caps])
|
|
|
|
results["total_findings"] = len(results["findings"])
|
|
print(json.dumps(results, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|