mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 13:44: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
215 lines
8.6 KiB
Python
215 lines
8.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Network Access Control (802.1X/NAC) monitoring agent using RADIUS and SNMP."""
|
|
|
|
import json
|
|
import sys
|
|
import argparse
|
|
from datetime import datetime
|
|
from collections import Counter
|
|
|
|
try:
|
|
from pyrad.client import Client
|
|
from pyrad.dictionary import Dictionary
|
|
from pyrad import packet
|
|
except ImportError:
|
|
print("Install pyrad: pip install pyrad")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
from pysnmp.hlapi import nextCmd, SnmpEngine, CommunityData, UdpTransportTarget, ContextData, ObjectType, ObjectIdentity
|
|
HAS_SNMP = True
|
|
except ImportError:
|
|
HAS_SNMP = False
|
|
|
|
|
|
def test_radius_auth(server, secret, username, password, port=1812):
|
|
"""Test RADIUS authentication for a user credential pair."""
|
|
srv = Client(server=server, secret=secret.encode(),
|
|
dict=Dictionary(dict_file=None))
|
|
srv.AuthPort = port
|
|
req = srv.CreateAuthPacket(code=packet.AccessRequest, User_Name=username)
|
|
req["User-Password"] = req.PwCrypt(password)
|
|
req["NAS-IP-Address"] = "192.168.1.1"
|
|
req["NAS-Port-Type"] = "Ethernet"
|
|
req["NAS-Port"] = 1
|
|
try:
|
|
reply = srv.SendPacket(req)
|
|
if reply.code == packet.AccessAccept:
|
|
attrs = {}
|
|
for key in reply.keys():
|
|
attrs[key] = reply[key]
|
|
return {"status": "ACCEPT", "user": username, "attributes": str(attrs)}
|
|
elif reply.code == packet.AccessReject:
|
|
return {"status": "REJECT", "user": username, "reason": "Invalid credentials"}
|
|
elif reply.code == packet.AccessChallenge:
|
|
return {"status": "CHALLENGE", "user": username, "reason": "Additional auth required"}
|
|
except Exception as e:
|
|
return {"status": "ERROR", "user": username, "error": str(e)}
|
|
|
|
|
|
def parse_radius_log(log_file, max_lines=1000):
|
|
"""Parse FreeRADIUS log file for authentication events."""
|
|
events = []
|
|
try:
|
|
with open(log_file, "r") as f:
|
|
for i, line in enumerate(f):
|
|
if i >= max_lines:
|
|
break
|
|
if "Auth:" in line or "Login" in line:
|
|
parts = line.strip().split()
|
|
event = {"raw": line.strip(), "timestamp": " ".join(parts[:3]) if len(parts) > 3 else ""}
|
|
if "Login OK" in line:
|
|
event["result"] = "SUCCESS"
|
|
elif "Login incorrect" in line:
|
|
event["result"] = "FAILURE"
|
|
elif "Invalid user" in line:
|
|
event["result"] = "INVALID_USER"
|
|
else:
|
|
event["result"] = "OTHER"
|
|
events.append(event)
|
|
except FileNotFoundError:
|
|
events.append({"error": f"Log file not found: {log_file}"})
|
|
return events
|
|
|
|
|
|
def check_switch_port_status(switch_ip, community="public"):
|
|
"""Query switch via SNMP for 802.1X port authentication status."""
|
|
if not HAS_SNMP:
|
|
return [{"error": "pysnmp not installed. Run: pip install pysnmp"}]
|
|
|
|
dot1x_auth_oid = "1.3.6.1.2.1.8802.1.1.1.1.2.1.1.1"
|
|
results = []
|
|
iterator = nextCmd(
|
|
SnmpEngine(), CommunityData(community),
|
|
UdpTransportTarget((switch_ip, 161)),
|
|
ContextData(),
|
|
ObjectType(ObjectIdentity(dot1x_auth_oid)),
|
|
maxRows=100)
|
|
|
|
for errorIndication, errorStatus, errorIndex, varBinds in iterator:
|
|
if errorIndication or errorStatus:
|
|
results.append({"error": str(errorIndication or errorStatus)})
|
|
break
|
|
for varBind in varBinds:
|
|
oid, value = varBind
|
|
port_index = str(oid).split(".")[-1]
|
|
auth_states = {1: "initialize", 2: "disconnected", 3: "connecting",
|
|
4: "authenticating", 5: "authenticated",
|
|
6: "aborting", 7: "held", 8: "forceAuth", 9: "forceUnauth"}
|
|
results.append({
|
|
"port": port_index,
|
|
"state": auth_states.get(int(value), f"unknown({value})"),
|
|
"state_code": int(value)
|
|
})
|
|
return results
|
|
|
|
|
|
def analyze_auth_events(events):
|
|
"""Analyze authentication events for security issues."""
|
|
result_counts = Counter(e.get("result", "UNKNOWN") for e in events)
|
|
total = len(events)
|
|
failures = result_counts.get("FAILURE", 0) + result_counts.get("INVALID_USER", 0)
|
|
success_rate = round((result_counts.get("SUCCESS", 0) / max(total, 1)) * 100, 1)
|
|
|
|
analysis = {
|
|
"total_events": total,
|
|
"successes": result_counts.get("SUCCESS", 0),
|
|
"failures": failures,
|
|
"invalid_users": result_counts.get("INVALID_USER", 0),
|
|
"success_rate": success_rate,
|
|
"risk_level": "HIGH" if failures > total * 0.3 else "MEDIUM" if failures > total * 0.1 else "LOW",
|
|
}
|
|
|
|
if failures > 20:
|
|
analysis["alert"] = "High number of authentication failures - possible brute force attack"
|
|
|
|
return analysis
|
|
|
|
|
|
def generate_nac_policy_check():
|
|
"""Generate a NAC compliance policy checklist."""
|
|
policies = [
|
|
{"check": "802.1X enforcement", "requirement": "All access ports configured for dot1x",
|
|
"standard": "PCI-DSS 1.2"},
|
|
{"check": "Guest VLAN isolation", "requirement": "Unauthenticated devices on restricted VLAN",
|
|
"standard": "NIST 800-53 AC-4"},
|
|
{"check": "MAB fallback", "requirement": "MAC Authentication Bypass for non-supplicant devices",
|
|
"standard": "Best Practice"},
|
|
{"check": "EAP-TLS certificates", "requirement": "Certificate-based auth for managed devices",
|
|
"standard": "NIST 800-53 IA-5"},
|
|
{"check": "Posture assessment", "requirement": "Endpoint compliance check before full access",
|
|
"standard": "PCI-DSS 5.3"},
|
|
{"check": "Dynamic VLAN assignment", "requirement": "Role-based VLAN via RADIUS attributes",
|
|
"standard": "NIST 800-53 AC-6"},
|
|
{"check": "Re-authentication timer", "requirement": "Periodic re-auth every 3600 seconds",
|
|
"standard": "Best Practice"},
|
|
{"check": "RADIUS accounting", "requirement": "Accounting enabled for audit trail",
|
|
"standard": "SOC 2 CC6.1"},
|
|
]
|
|
return policies
|
|
|
|
|
|
def run_nac_audit(radius_log=None, switch_ip=None, community="public"):
|
|
"""Run NAC security audit."""
|
|
print(f"\n{'='*60}")
|
|
print(f" NETWORK ACCESS CONTROL AUDIT")
|
|
print(f" Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
|
print(f"{'='*60}\n")
|
|
|
|
policies = generate_nac_policy_check()
|
|
print(f"--- NAC POLICY CHECKLIST ---")
|
|
for p in policies:
|
|
print(f" [ ] {p['check']}: {p['requirement']} ({p['standard']})")
|
|
|
|
if radius_log:
|
|
events = parse_radius_log(radius_log)
|
|
analysis = analyze_auth_events(events)
|
|
print(f"\n--- RADIUS AUTH ANALYSIS ---")
|
|
print(f" Total Events: {analysis['total_events']}")
|
|
print(f" Successes: {analysis['successes']}")
|
|
print(f" Failures: {analysis['failures']}")
|
|
print(f" Success Rate: {analysis['success_rate']}%")
|
|
print(f" Risk Level: {analysis['risk_level']}")
|
|
if analysis.get("alert"):
|
|
print(f" ALERT: {analysis['alert']}")
|
|
|
|
if switch_ip:
|
|
ports = check_switch_port_status(switch_ip, community)
|
|
print(f"\n--- SWITCH PORT STATUS ({switch_ip}) ---")
|
|
for p in ports[:20]:
|
|
if "error" in p:
|
|
print(f" Error: {p['error']}")
|
|
else:
|
|
icon = "[OK]" if p["state"] == "authenticated" else "[!!]"
|
|
print(f" {icon} Port {p['port']}: {p['state']}")
|
|
|
|
print(f"\n{'='*60}\n")
|
|
return {"policies": policies}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Network Access Control Agent")
|
|
parser.add_argument("--audit", action="store_true", help="Run NAC audit")
|
|
parser.add_argument("--radius-log", help="Path to FreeRADIUS log file")
|
|
parser.add_argument("--switch", help="Switch IP for SNMP 802.1X status check")
|
|
parser.add_argument("--community", default="public", help="SNMP community string")
|
|
parser.add_argument("--test-auth", nargs=4, metavar=("SERVER", "SECRET", "USER", "PASS"),
|
|
help="Test RADIUS authentication")
|
|
parser.add_argument("--output", help="Save report to JSON")
|
|
args = parser.parse_args()
|
|
|
|
if args.test_auth:
|
|
result = test_radius_auth(*args.test_auth)
|
|
print(json.dumps(result, indent=2))
|
|
elif args.audit:
|
|
report = run_nac_audit(args.radius_log, args.switch, args.community)
|
|
if args.output:
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|