#!/usr/bin/env python3
"""Agent for detecting and testing AD CS ESC1 misconfiguration (enrollee supplies SAN)."""

import argparse
import json
import subprocess
from datetime import datetime, timezone


def enumerate_certificate_templates():
    """Enumerate AD CS certificate templates via certutil or Certipy."""
    templates = []
    try:
        result = subprocess.check_output(
            ["certutil", "-v", "-template"],
            text=True, errors="replace", timeout=30
        )
        current = {}
        for line in result.splitlines():
            line = line.strip()
            if line.startswith("Template["):
                if current:
                    templates.append(current)
                current = {"raw_lines": []}
            if ":" in line:
                key, _, val = line.partition(":")
                current[key.strip()] = val.strip()
            if current:
                current["raw_lines"].append(line)
        if current:
            templates.append(current)
    except (subprocess.SubprocessError, FileNotFoundError):
        pass
    return templates


def check_esc1_vulnerable(template):
    """Check if a certificate template is vulnerable to ESC1."""
    indicators = []
    raw = "\n".join(template.get("raw_lines", []))
    if "CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT" in raw or "ENROLLEE_SUPPLIES_SUBJECT" in raw:
        indicators.append("Enrollee can supply Subject Alternative Name (SAN)")
    if "Client Authentication" in raw or "1.3.6.1.5.5.7.3.2" in raw:
        indicators.append("Template allows Client Authentication EKU")
    if "Domain Users" in raw or "Authenticated Users" in raw:
        indicators.append("Low-privileged users can enroll")
    if "Manager approval" not in raw and "manager approval" not in raw.lower():
        indicators.append("No manager approval required")
    return indicators


def run_certipy_find(domain, username, password, dc_ip):
    """Run Certipy to find vulnerable certificate templates."""
    findings = []
    try:
        result = subprocess.check_output(
            ["certipy", "find", "-u", f"{username}@{domain}",
             "-p", password, "-dc-ip", dc_ip, "-vulnerable", "-json"],
            text=True, errors="replace", timeout=60
        )
        data = json.loads(result)
        for tmpl in data.get("Certificate Templates", []):
            if "ESC1" in str(tmpl.get("Vulnerabilities", [])):
                findings.append({
                    "template": tmpl.get("Template Name", ""),
                    "vulnerability": "ESC1",
                    "enrollable_by": tmpl.get("Enrollment Rights", []),
                })
    except (subprocess.SubprocessError, json.JSONDecodeError, FileNotFoundError):
        pass
    return findings


def main():
    parser = argparse.ArgumentParser(
        description="Detect AD CS ESC1 vulnerability (authorized pentesting only)"
    )
    parser.add_argument("--certutil", action="store_true", help="Use certutil enumeration")
    parser.add_argument("--certipy", action="store_true", help="Use Certipy tool")
    parser.add_argument("--domain", help="AD domain name")
    parser.add_argument("--username", help="Domain username")
    parser.add_argument("--password", help="Domain password")
    parser.add_argument("--dc-ip", help="Domain controller IP")
    parser.add_argument("--output", "-o", help="Output JSON report")
    args = parser.parse_args()

    print("[*] AD CS ESC1 Vulnerability Detection Agent")
    print("[!] For authorized security testing only")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []}

    if args.certutil:
        templates = enumerate_certificate_templates()
        for tmpl in templates:
            vulns = check_esc1_vulnerable(tmpl)
            if vulns:
                report["findings"].append({
                    "template": tmpl.get("Template", "Unknown"),
                    "esc1_indicators": vulns,
                    "indicator_count": len(vulns),
                })
        print(f"[*] Templates analyzed: {len(templates)}, ESC1 vulnerable: {len(report['findings'])}")

    if args.certipy and args.domain:
        certipy_results = run_certipy_find(
            args.domain, args.username or "", args.password or "", args.dc_ip or ""
        )
        report["findings"].extend(certipy_results)
        print(f"[*] Certipy findings: {len(certipy_results)}")

    report["risk_level"] = "CRITICAL" if report["findings"] 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()
