mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24: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
214 lines
8.0 KiB
Python
214 lines
8.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for auditing and configuring Tofino ICS firewall rules."""
|
|
|
|
import json
|
|
import argparse
|
|
from datetime import datetime
|
|
from collections import Counter
|
|
|
|
|
|
OT_PROTOCOLS = {
|
|
"modbus": {"port": 502, "layer": "TCP", "dpi": True},
|
|
"enip": {"port": 44818, "layer": "TCP/UDP", "dpi": True},
|
|
"dnp3": {"port": 20000, "layer": "TCP", "dpi": True},
|
|
"opc_ua": {"port": 4840, "layer": "TCP", "dpi": True},
|
|
"s7comm": {"port": 102, "layer": "TCP", "dpi": True},
|
|
"bacnet": {"port": 47808, "layer": "UDP", "dpi": True},
|
|
"iec61850_mms": {"port": 102, "layer": "TCP", "dpi": True},
|
|
"profinet": {"port": 34964, "layer": "UDP", "dpi": False},
|
|
}
|
|
|
|
RISKY_MODBUS_FUNCTIONS = {
|
|
5: "Write Single Coil",
|
|
6: "Write Single Register",
|
|
15: "Write Multiple Coils",
|
|
16: "Write Multiple Registers",
|
|
22: "Mask Write Register",
|
|
23: "Read/Write Multiple Registers",
|
|
}
|
|
|
|
|
|
def audit_firewall_rules(rules_path):
|
|
"""Audit Tofino firewall rules for security issues."""
|
|
with open(rules_path) as f:
|
|
rules = json.load(f)
|
|
rule_list = rules if isinstance(rules, list) else rules.get("rules", [])
|
|
findings = []
|
|
|
|
for rule in rule_list:
|
|
action = rule.get("action", "").lower()
|
|
src = rule.get("source", rule.get("src", "any"))
|
|
dst = rule.get("destination", rule.get("dst", "any"))
|
|
protocol = rule.get("protocol", "").lower()
|
|
port = rule.get("port", rule.get("dst_port", "any"))
|
|
|
|
if src == "any" and dst == "any" and action == "allow":
|
|
findings.append({
|
|
"rule": rule.get("id", rule.get("name", "")),
|
|
"issue": "Allow-any-any rule detected",
|
|
"severity": "CRITICAL",
|
|
"recommendation": "Restrict to specific source/destination",
|
|
})
|
|
|
|
if protocol in ("modbus", "enip", "s7comm") and not rule.get("dpi_enabled", False):
|
|
findings.append({
|
|
"rule": rule.get("id", ""),
|
|
"issue": f"DPI not enabled for {protocol}",
|
|
"severity": "HIGH",
|
|
"recommendation": f"Enable deep packet inspection for {protocol}",
|
|
})
|
|
|
|
if protocol == "modbus":
|
|
allowed_funcs = rule.get("allowed_functions", [])
|
|
risky = [f for f in allowed_funcs if f in RISKY_MODBUS_FUNCTIONS]
|
|
if risky:
|
|
findings.append({
|
|
"rule": rule.get("id", ""),
|
|
"issue": f"Write functions allowed: {risky}",
|
|
"severity": "HIGH",
|
|
"detail": {fc: RISKY_MODBUS_FUNCTIONS[fc] for fc in risky},
|
|
})
|
|
|
|
if action == "allow" and not rule.get("logging", False):
|
|
findings.append({
|
|
"rule": rule.get("id", ""),
|
|
"issue": "Allow rule without logging",
|
|
"severity": "MEDIUM",
|
|
})
|
|
|
|
has_deny_all = any(r.get("action", "").lower() == "deny" and
|
|
r.get("source", "any") == "any" and
|
|
r.get("destination", "any") == "any" for r in rule_list)
|
|
if not has_deny_all:
|
|
findings.append({
|
|
"issue": "No default deny-all rule found",
|
|
"severity": "CRITICAL",
|
|
"recommendation": "Add deny-all as last rule",
|
|
})
|
|
|
|
return findings
|
|
|
|
|
|
def analyze_ot_traffic_log(log_path):
|
|
"""Analyze OT network traffic log for anomalies."""
|
|
with open(log_path) as f:
|
|
entries = json.load(f)
|
|
items = entries if isinstance(entries, list) else entries.get("flows", [])
|
|
|
|
by_protocol = Counter()
|
|
by_src = Counter()
|
|
anomalies = []
|
|
|
|
for entry in items:
|
|
proto = entry.get("protocol", entry.get("app_protocol", "unknown")).lower()
|
|
by_protocol[proto] += 1
|
|
by_src[entry.get("src_ip", "unknown")] += 1
|
|
|
|
port = entry.get("dst_port", 0)
|
|
if proto == "modbus" and port != 502:
|
|
anomalies.append({"type": "non_standard_port", "protocol": proto,
|
|
"port": port, "severity": "HIGH"})
|
|
|
|
if entry.get("action", "").lower() == "denied":
|
|
anomalies.append({
|
|
"type": "denied_connection",
|
|
"src": entry.get("src_ip", ""),
|
|
"dst": entry.get("dst_ip", ""),
|
|
"protocol": proto,
|
|
"severity": "MEDIUM",
|
|
})
|
|
|
|
return {
|
|
"total_flows": len(items),
|
|
"by_protocol": dict(by_protocol),
|
|
"unique_sources": len(by_src),
|
|
"anomalies": anomalies[:50],
|
|
"anomaly_count": len(anomalies),
|
|
}
|
|
|
|
|
|
def generate_zone_rules(zone_config):
|
|
"""Generate Tofino firewall rules from zone configuration."""
|
|
rules = []
|
|
zones = zone_config.get("zones", [])
|
|
for zone in zones:
|
|
zone_name = zone.get("name", "")
|
|
allowed_protocols = zone.get("allowed_protocols", [])
|
|
plc_ips = zone.get("plc_ips", [])
|
|
hmi_ips = zone.get("hmi_ips", [])
|
|
eng_ips = zone.get("engineering_ips", [])
|
|
|
|
for proto in allowed_protocols:
|
|
proto_info = OT_PROTOCOLS.get(proto, {})
|
|
port = proto_info.get("port", "any")
|
|
for hmi in hmi_ips:
|
|
for plc in plc_ips:
|
|
rules.append({
|
|
"zone": zone_name,
|
|
"action": "allow",
|
|
"source": hmi,
|
|
"destination": plc,
|
|
"protocol": proto,
|
|
"port": port,
|
|
"dpi_enabled": proto_info.get("dpi", False),
|
|
"logging": True,
|
|
})
|
|
if proto == "modbus":
|
|
for eng in eng_ips:
|
|
for plc in plc_ips:
|
|
rules.append({
|
|
"zone": zone_name,
|
|
"action": "allow",
|
|
"source": eng,
|
|
"destination": plc,
|
|
"protocol": "modbus",
|
|
"port": 502,
|
|
"dpi_enabled": True,
|
|
"allowed_functions": [1, 2, 3, 4],
|
|
"logging": True,
|
|
"comment": "Read-only Modbus for engineering",
|
|
})
|
|
|
|
rules.append({"action": "deny", "source": "any", "destination": "any",
|
|
"protocol": "any", "logging": True, "comment": "Default deny-all"})
|
|
return rules
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Tofino ICS Firewall Agent")
|
|
parser.add_argument("--rules", help="Firewall rules JSON to audit")
|
|
parser.add_argument("--traffic-log", help="OT traffic log JSON")
|
|
parser.add_argument("--zone-config", help="Zone config JSON for rule generation")
|
|
parser.add_argument("--action", choices=["audit", "traffic", "generate", "full"],
|
|
default="full")
|
|
parser.add_argument("--output", default="tofino_ics_firewall_report.json")
|
|
args = parser.parse_args()
|
|
|
|
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
|
|
|
|
if args.action in ("audit", "full") and args.rules:
|
|
findings = audit_firewall_rules(args.rules)
|
|
report["results"]["audit"] = findings
|
|
critical = sum(1 for f in findings if f.get("severity") == "CRITICAL")
|
|
print(f"[+] Audit: {len(findings)} findings, {critical} critical")
|
|
|
|
if args.action in ("traffic", "full") and args.traffic_log:
|
|
result = analyze_ot_traffic_log(args.traffic_log)
|
|
report["results"]["traffic"] = result
|
|
print(f"[+] Traffic: {result['total_flows']} flows, {result['anomaly_count']} anomalies")
|
|
|
|
if args.action in ("generate", "full") and args.zone_config:
|
|
with open(args.zone_config) as f:
|
|
zc = json.load(f)
|
|
rules = generate_zone_rules(zc)
|
|
report["results"]["generated_rules"] = rules
|
|
print(f"[+] Generated {len(rules)} firewall rules")
|
|
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
print(f"[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|