#!/usr/bin/env python3 """Agent for detecting Zerologon (CVE-2020-1472) vulnerability — authorized testing only.""" import argparse import json import subprocess import sys from datetime import datetime, timezone def check_zerologon_nmap(dc_ip): """Use nmap script to check for Zerologon vulnerability.""" try: result = subprocess.check_output( ["nmap", "-p", "135,445", "--script", "smb-vuln-cve-2020-1472", dc_ip], text=True, errors="replace", timeout=30 ) return { "method": "nmap", "target": dc_ip, "vulnerable": "VULNERABLE" in result.upper(), "output": result[:500], } except (subprocess.SubprocessError, FileNotFoundError): return {"method": "nmap", "status": "nmap not available"} def check_zerologon_impacket(dc_name, dc_ip): """Check vulnerability using zerologon_tester.py from Impacket.""" try: result = subprocess.check_output( ["zerologon_tester.py", dc_name, dc_ip], text=True, errors="replace", timeout=30 ) return { "method": "zerologon_tester", "target": dc_ip, "dc_name": dc_name, "vulnerable": "success" in result.lower() or "vulnerable" in result.lower(), "output": result[:500], } except (subprocess.SubprocessError, FileNotFoundError): return {"method": "zerologon_tester", "status": "tool not available"} def check_patch_status(dc_ip): """Check if DC has Zerologon patches applied.""" if sys.platform != "win32": return {"status": "non-windows — use remote check"} try: result = subprocess.check_output( ["wmic", "/node:" + dc_ip, "qfe", "list", "brief"], text=True, errors="replace", timeout=15 ) patches = ["KB4571694", "KB4571703", "KB4571723", "KB4571736"] found = [kb for kb in patches if kb in result] return { "target": dc_ip, "patched": len(found) > 0, "patches_found": found, "patches_checked": patches, } except subprocess.SubprocessError: return {"status": "wmic check failed"} def check_secure_channel(dc_ip): """Verify Netlogon secure channel is enforced.""" ps_cmd = ( f"Get-ItemProperty 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters' " f"-Name FullSecureChannelProtection -ErrorAction SilentlyContinue | " f"Select-Object FullSecureChannelProtection | ConvertTo-Json" ) try: result = subprocess.check_output( ["powershell", "-NoProfile", "-Command", ps_cmd], text=True, errors="replace", timeout=10 ) data = json.loads(result) if result.strip() else {} enforced = data.get("FullSecureChannelProtection", 0) == 1 return {"secure_channel_enforced": enforced} except (subprocess.SubprocessError, json.JSONDecodeError): return {"status": "check_failed"} def main(): parser = argparse.ArgumentParser( description="Detect Zerologon CVE-2020-1472 (authorized testing only)" ) parser.add_argument("--dc-ip", required=True, help="Domain controller IP") parser.add_argument("--dc-name", help="DC NetBIOS name (for Impacket check)") parser.add_argument("--nmap", action="store_true", help="Use nmap check") parser.add_argument("--check-patch", action="store_true") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] Zerologon (CVE-2020-1472) Detection Agent") print("[!] For authorized security testing only") report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}} if args.nmap: result = check_zerologon_nmap(args.dc_ip) report["findings"]["nmap"] = result print(f"[*] nmap check: vulnerable={result.get('vulnerable', 'unknown')}") if args.dc_name: result = check_zerologon_impacket(args.dc_name, args.dc_ip) report["findings"]["impacket"] = result print(f"[*] Impacket check: {result.get('vulnerable', result.get('status'))}") if args.check_patch: patch = check_patch_status(args.dc_ip) report["findings"]["patch_status"] = patch channel = check_secure_channel(args.dc_ip) report["findings"]["secure_channel"] = channel report["risk_level"] = "CRITICAL" if any( v.get("vulnerable") for v in report["findings"].values() if isinstance(v, dict) ) else "LOW" if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2) print(f"[*] Report saved to {args.output}") else: print(json.dumps(report, indent=2)) if __name__ == "__main__": main()