mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-13 22:54:53 +03:00
233 lines
8.3 KiB
Python
233 lines
8.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Falco Container Escape Rule Manager - Generate, validate, and deploy
|
|
custom Falco rules for container escape detection.
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import argparse
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
# Container escape detection rule templates
|
|
ESCAPE_RULES = {
|
|
"mount_host_fs": {
|
|
"rule": "Container Mounting Host Filesystem",
|
|
"desc": "Detect a container attempting to mount the host filesystem",
|
|
"condition": 'spawned_process and container and proc.name = mount and (proc.args contains "/host" or proc.args contains "nsenter")',
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "escape", "T1611"],
|
|
},
|
|
"nsenter_usage": {
|
|
"rule": "Nsenter Execution in Container",
|
|
"desc": "Detect nsenter being used to escape container namespaces",
|
|
"condition": "spawned_process and container and proc.name = nsenter",
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "escape", "namespace", "T1611"],
|
|
},
|
|
"privileged_container": {
|
|
"rule": "Launch Privileged Container",
|
|
"desc": "Detect a privileged container being launched",
|
|
"condition": "container_started and container and container.privileged=true",
|
|
"priority": "WARNING",
|
|
"tags": ["container", "privileged", "T1610"],
|
|
},
|
|
"cgroup_escape": {
|
|
"rule": "Write to Cgroup Release Agent",
|
|
"desc": "Detect writes to cgroup release_agent - known escape vector",
|
|
"condition": "open_write and container and fd.name endswith release_agent",
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "escape", "cgroup", "CVE-2022-0492"],
|
|
},
|
|
"docker_socket": {
|
|
"rule": "Container Accessing Docker Socket",
|
|
"desc": "Detect container accessing Docker socket for host control",
|
|
"condition": "(open_read or open_write) and container and fd.name = /var/run/docker.sock",
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "escape", "docker-socket", "T1610"],
|
|
},
|
|
"kernel_module": {
|
|
"rule": "Container Loading Kernel Module",
|
|
"desc": "Detect a container attempting to load a kernel module",
|
|
"condition": "spawned_process and container and proc.name in (insmod, modprobe)",
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "escape", "kernel", "T1611"],
|
|
},
|
|
"shadow_file": {
|
|
"rule": "Container Reading Host Shadow File",
|
|
"desc": "Detect container reading /etc/shadow",
|
|
"condition": 'open_read and container and (fd.name = /etc/shadow or fd.name startswith /host/etc/shadow)',
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "credential-access", "T1003"],
|
|
},
|
|
"sysrq_trigger": {
|
|
"rule": "Write to Sysrq Trigger from Container",
|
|
"desc": "Detect writes to /proc/sysrq-trigger from container",
|
|
"condition": "open_write and container and fd.name = /proc/sysrq-trigger",
|
|
"priority": "CRITICAL",
|
|
"tags": ["container", "escape", "host-manipulation"],
|
|
},
|
|
}
|
|
|
|
|
|
def generate_rule_yaml(rule_key: str, rule_data: dict) -> str:
|
|
"""Generate a single Falco rule in YAML format."""
|
|
tags_str = ", ".join(rule_data["tags"])
|
|
output_fields = (
|
|
"user=%user.name container_id=%container.id "
|
|
"container_name=%container.name image=%container.image.repository "
|
|
"command=%proc.cmdline"
|
|
)
|
|
return f"""- rule: {rule_data['rule']}
|
|
desc: {rule_data['desc']}
|
|
condition: >
|
|
{rule_data['condition']}
|
|
output: >
|
|
{rule_data['desc']}
|
|
({output_fields})
|
|
priority: {rule_data['priority']}
|
|
tags: [{tags_str}]
|
|
"""
|
|
|
|
|
|
def generate_all_rules() -> str:
|
|
"""Generate complete Falco rules file for container escape detection."""
|
|
header = f"""# Container Escape Detection Rules for Falco
|
|
# Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')}
|
|
# Deploy to: /etc/falco/rules.d/container-escape.yaml
|
|
|
|
- list: escape_binaries
|
|
items: [nsenter, chroot, unshare, mount, umount, pivot_root]
|
|
|
|
- macro: container_escape_binary
|
|
condition: spawned_process and container and proc.name in (escape_binaries)
|
|
|
|
"""
|
|
rules = ""
|
|
for key, data in ESCAPE_RULES.items():
|
|
rules += generate_rule_yaml(key, data) + "\n"
|
|
|
|
return header + rules
|
|
|
|
|
|
def parse_falco_alerts(log_file: str) -> list:
|
|
"""Parse Falco JSON alerts from log file."""
|
|
alerts = []
|
|
path = Path(log_file)
|
|
if not path.exists():
|
|
print(f"Log file not found: {log_file}", file=sys.stderr)
|
|
return alerts
|
|
|
|
for line in path.read_text().splitlines():
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
alert = json.loads(line)
|
|
alerts.append({
|
|
"time": alert.get("time", ""),
|
|
"rule": alert.get("rule", ""),
|
|
"priority": alert.get("priority", ""),
|
|
"output": alert.get("output", ""),
|
|
"tags": alert.get("tags", []),
|
|
"container_name": alert.get("output_fields", {}).get("container.name", ""),
|
|
"container_image": alert.get("output_fields", {}).get("container.image.repository", ""),
|
|
})
|
|
except json.JSONDecodeError:
|
|
continue
|
|
|
|
return alerts
|
|
|
|
|
|
def summarize_alerts(alerts: list) -> dict:
|
|
"""Summarize Falco alerts by rule and severity."""
|
|
summary = {"total": len(alerts), "by_priority": {}, "by_rule": {}, "escape_attempts": []}
|
|
|
|
for alert in alerts:
|
|
pri = alert["priority"]
|
|
rule = alert["rule"]
|
|
summary["by_priority"][pri] = summary["by_priority"].get(pri, 0) + 1
|
|
summary["by_rule"][rule] = summary["by_rule"].get(rule, 0) + 1
|
|
|
|
if any(tag in alert.get("tags", []) for tag in ["escape", "T1611", "T1610"]):
|
|
summary["escape_attempts"].append(alert)
|
|
|
|
return summary
|
|
|
|
|
|
def check_falco_health() -> dict:
|
|
"""Check if Falco is running and healthy."""
|
|
result = subprocess.run(
|
|
["kubectl", "get", "pods", "-n", "falco", "-l", "app.kubernetes.io/name=falco",
|
|
"-o", "json"],
|
|
capture_output=True, text=True,
|
|
)
|
|
if result.returncode != 0:
|
|
return {"healthy": False, "error": result.stderr}
|
|
|
|
pods = json.loads(result.stdout)
|
|
pod_status = []
|
|
for pod in pods.get("items", []):
|
|
name = pod["metadata"]["name"]
|
|
phase = pod["status"]["phase"]
|
|
node = pod["spec"].get("nodeName", "unknown")
|
|
pod_status.append({"name": name, "phase": phase, "node": node})
|
|
|
|
all_running = all(p["phase"] == "Running" for p in pod_status)
|
|
return {"healthy": all_running, "pods": pod_status}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Falco Container Escape Rule Manager")
|
|
subparsers = parser.add_subparsers(dest="command")
|
|
|
|
subparsers.add_parser("generate", help="Generate container escape detection rules")
|
|
|
|
parse_cmd = subparsers.add_parser("parse-alerts", help="Parse Falco alert logs")
|
|
parse_cmd.add_argument("--log-file", required=True, help="Path to Falco JSON log file")
|
|
|
|
subparsers.add_parser("health", help="Check Falco deployment health")
|
|
|
|
deploy_cmd = subparsers.add_parser("deploy", help="Deploy rules to Kubernetes")
|
|
deploy_cmd.add_argument("--namespace", default="falco", help="Falco namespace")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.command == "generate":
|
|
print(generate_all_rules())
|
|
|
|
elif args.command == "parse-alerts":
|
|
alerts = parse_falco_alerts(args.log_file)
|
|
summary = summarize_alerts(alerts)
|
|
print(json.dumps(summary, indent=2))
|
|
|
|
elif args.command == "health":
|
|
health = check_falco_health()
|
|
print(json.dumps(health, indent=2))
|
|
|
|
elif args.command == "deploy":
|
|
rules_yaml = generate_all_rules()
|
|
proc = subprocess.run(
|
|
["kubectl", "create", "configmap", "falco-escape-rules",
|
|
"-n", args.namespace, "--from-literal=container-escape.yaml=" + rules_yaml,
|
|
"--dry-run=client", "-o", "yaml"],
|
|
capture_output=True, text=True,
|
|
)
|
|
apply = subprocess.run(
|
|
["kubectl", "apply", "-f", "-"],
|
|
input=proc.stdout, capture_output=True, text=True,
|
|
)
|
|
print(apply.stdout)
|
|
if apply.returncode == 0:
|
|
print("Rules deployed. Restart Falco to load:")
|
|
print(f" kubectl rollout restart daemonset/falco -n {args.namespace}")
|
|
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|