mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-29 23:46:53 +03:00
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
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for performing OT network security assessment based on Purdue model."""
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import csv
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from collections import Counter
|
||||
|
||||
|
||||
PURDUE_LEVELS = {
|
||||
0: "Physical Process (sensors, actuators)",
|
||||
1: "Basic Control (PLC, RTU, SIS)",
|
||||
2: "Area Supervisory (HMI, SCADA, DCS)",
|
||||
3: "Site Operations (Historian, Patch Mgmt)",
|
||||
3.5: "IT/OT DMZ (jump servers, data diode)",
|
||||
4: "Business Planning (ERP, Email)",
|
||||
5: "Enterprise Network (Internet, Cloud)",
|
||||
}
|
||||
|
||||
|
||||
def assess_asset_inventory(csv_file):
|
||||
"""Assess OT asset inventory against Purdue model zones."""
|
||||
with open(csv_file, "r", encoding="utf-8", errors="replace") as f:
|
||||
reader = csv.DictReader(f)
|
||||
assets = list(reader)
|
||||
by_level = {}
|
||||
by_vendor = Counter()
|
||||
by_protocol = Counter()
|
||||
findings = []
|
||||
for asset in assets:
|
||||
level = asset.get("purdue_level", asset.get("zone", "unknown"))
|
||||
by_level.setdefault(str(level), []).append(asset)
|
||||
by_vendor[asset.get("vendor", "unknown")] += 1
|
||||
proto = asset.get("protocol", asset.get("protocols", ""))
|
||||
for p in proto.split(","):
|
||||
if p.strip():
|
||||
by_protocol[p.strip()] += 1
|
||||
firmware = asset.get("firmware_version", "")
|
||||
if asset.get("end_of_life", "").lower() in ("yes", "true"):
|
||||
findings.append({"asset": asset.get("name", ""), "issue": "END_OF_LIFE", "severity": "HIGH"})
|
||||
if not firmware:
|
||||
findings.append({"asset": asset.get("name", ""), "issue": "UNKNOWN_FIRMWARE", "severity": "MEDIUM"})
|
||||
return {
|
||||
"total_assets": len(assets),
|
||||
"by_purdue_level": {k: len(v) for k, v in by_level.items()},
|
||||
"by_vendor": dict(by_vendor.most_common(10)),
|
||||
"protocols": dict(by_protocol.most_common(15)),
|
||||
"findings": findings[:20],
|
||||
}
|
||||
|
||||
|
||||
def assess_network_segmentation(csv_file):
|
||||
"""Check OT network segmentation and firewall rules."""
|
||||
with open(csv_file, "r", encoding="utf-8", errors="replace") as f:
|
||||
reader = csv.DictReader(f)
|
||||
rules = list(reader)
|
||||
findings = []
|
||||
for rule in rules:
|
||||
src_zone = rule.get("src_zone", "")
|
||||
dst_zone = rule.get("dst_zone", "")
|
||||
action = rule.get("action", "").lower()
|
||||
protocol = rule.get("protocol", "")
|
||||
if action == "allow" and src_zone.startswith("IT") and dst_zone.startswith("OT"):
|
||||
findings.append({
|
||||
"rule": rule.get("name", rule.get("id", "")),
|
||||
"issue": "DIRECT_IT_TO_OT_ACCESS",
|
||||
"severity": "CRITICAL",
|
||||
"src_zone": src_zone, "dst_zone": dst_zone,
|
||||
})
|
||||
if action == "allow" and protocol.lower() in ("any", "all", "*"):
|
||||
findings.append({
|
||||
"rule": rule.get("name", rule.get("id", "")),
|
||||
"issue": "ALLOW_ANY_PROTOCOL",
|
||||
"severity": "HIGH",
|
||||
})
|
||||
return {
|
||||
"total_rules": len(rules),
|
||||
"allow_rules": sum(1 for r in rules if r.get("action", "").lower() == "allow"),
|
||||
"deny_rules": sum(1 for r in rules if r.get("action", "").lower() in ("deny", "drop")),
|
||||
"findings": findings[:20],
|
||||
"dmz_rules": sum(1 for r in rules if "dmz" in r.get("src_zone", "").lower() or "dmz" in r.get("dst_zone", "").lower()),
|
||||
}
|
||||
|
||||
|
||||
def scan_ot_protocols(target_subnet):
|
||||
"""Scan for common OT protocols on a subnet using nmap."""
|
||||
ot_ports = "102,502,2222,4840,20000,44818,47808"
|
||||
cmd = ["nmap", "-sV", "-p", ot_ports, target_subnet, "-oX", "-", "--open"]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
||||
import xml.etree.ElementTree as ET
|
||||
root = ET.fromstring(result.stdout)
|
||||
hosts = []
|
||||
for host in root.findall(".//host"):
|
||||
addr = host.find("address").get("addr", "") if host.find("address") is not None else ""
|
||||
ports = []
|
||||
for port in host.findall(".//port"):
|
||||
service = port.find("service")
|
||||
ports.append({
|
||||
"port": int(port.get("portid", 0)),
|
||||
"protocol": service.get("name", "") if service is not None else "",
|
||||
"product": service.get("product", "") if service is not None else "",
|
||||
})
|
||||
if ports:
|
||||
hosts.append({"ip": addr, "ot_services": ports})
|
||||
protocol_map = {502: "Modbus", 102: "S7Comm", 44818: "EtherNet/IP", 4840: "OPC-UA", 47808: "BACnet", 20000: "DNP3"}
|
||||
return {"subnet": target_subnet, "hosts_with_ot": len(hosts), "hosts": hosts, "protocol_reference": protocol_map}
|
||||
except FileNotFoundError:
|
||||
return {"error": "nmap not installed"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def generate_assessment_report(asset_csv, firewall_csv=None):
|
||||
"""Generate comprehensive OT security assessment."""
|
||||
report = {
|
||||
"generated": datetime.utcnow().isoformat(),
|
||||
"frameworks": ["IEC 62443", "NIST SP 800-82", "NERC CIP"],
|
||||
"asset_assessment": assess_asset_inventory(asset_csv),
|
||||
}
|
||||
if firewall_csv:
|
||||
report["segmentation"] = assess_network_segmentation(firewall_csv)
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="OT Network Security Assessment Agent")
|
||||
sub = parser.add_subparsers(dest="command")
|
||||
a = sub.add_parser("assets", help="Assess OT asset inventory")
|
||||
a.add_argument("--csv", required=True)
|
||||
s = sub.add_parser("segmentation", help="Assess network segmentation")
|
||||
s.add_argument("--csv", required=True, help="Firewall rules CSV")
|
||||
p = sub.add_parser("protocols", help="Scan for OT protocols")
|
||||
p.add_argument("--subnet", required=True)
|
||||
r = sub.add_parser("report", help="Full assessment report")
|
||||
r.add_argument("--assets", required=True)
|
||||
r.add_argument("--firewall", help="Firewall rules CSV")
|
||||
args = parser.parse_args()
|
||||
if args.command == "assets":
|
||||
result = assess_asset_inventory(args.csv)
|
||||
elif args.command == "segmentation":
|
||||
result = assess_network_segmentation(args.csv)
|
||||
elif args.command == "protocols":
|
||||
result = scan_ot_protocols(args.subnet)
|
||||
elif args.command == "report":
|
||||
result = generate_assessment_report(args.assets, args.firewall)
|
||||
else:
|
||||
parser.print_help()
|
||||
return
|
||||
print(json.dumps(result, indent=2, default=str))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user