Skip to content

AD-KRB-003: User accounts with SPNs (Kerberoasting)

medium high   Kerberos   MITRE ATT&CK T1558.003   Reads: LDAP

Enabled user accounts with a service principal name, whose service tickets any domain user can request and crack offline.

Also known as: kerberoasting, kerberoast

Why it matters

Any authenticated user can ask the KDC for a service ticket to any service principal name (SPN) in the domain. Part of that ticket is encrypted with a key derived from the password of the account that owns the SPN. When the owner is a user account, its password was chosen by a person and can be guessed offline, with no failed logons and no lockout. RC4-encrypted tickets (etype 23) crack far faster than AES ones, which is why AD-KRB-005 makes this worse.

A cracked service account password gives the attacker that account, and service accounts are often over-privileged: a Kerberoastable member of Domain Admins is a one-step domain compromise.

How ad-doctor detects it

From the LDAP snapshot: every enabled user object with at least one value in servicePrincipalName, excluding krbtgt. Computer accounts and managed service accounts (gMSA, sMSA, dMSA) are skipped because their passwords are long random values that the system rotates.

Privilege comes from the transitive membership of the tier-0 groups collected in the same snapshot and from adminCount.

ad-doctor does not request any service ticket: the check is a pure function of the directory data, so running it leaves no 4769 events behind.

Attributes and probes read
  • servicePrincipalName
  • userAccountControl
  • adminCount
  • objectClass

Severity

High when at least one reported account is privileged: a transitive member of a tier-0 group, or an account with adminCount = 1. Medium otherwise.

False positives and limits

  • Long random passwords. A user account whose password is 25+ random characters from a vault, rotated regularly, is not practically crackable. ad-doctor cannot see password strength, so it still reports the account. Record the decision in an accepted-risk file with an owner and an expiry date.
  • Honeypot accounts created on purpose to catch Kerberoasting: accept them the same way.
  • Stale SPNs left behind by uninstalled software are true positives: the fix is to remove the SPN.

Fix

  1. List the accounts and how they are configured.

    Get-ADUser -LDAPFilter '(&(servicePrincipalName=*)(!(sAMAccountName=krbtgt)))' `
        -Properties servicePrincipalName, PasswordLastSet, adminCount, 'msDS-SupportedEncryptionTypes' |
        Select-Object SamAccountName, PasswordLastSet, adminCount, 'msDS-SupportedEncryptionTypes',
            @{n = 'SPNs'; e = { $_.servicePrincipalName -join ', ' }}
    
  2. Remove SPNs nothing uses any more.

    Set-ADUser svc_web -ServicePrincipalNames @{Remove = 'HTTP/web01.corp.example'} -WhatIf
    
  3. Move services to a group managed service account (gMSA). The domain needs a KDS root key once (Add-KdsRootKey -EffectiveImmediately, then wait for replication). Remove the SPN from the old account first, since an SPN must be unique.

    New-ADServiceAccount -Name gmsa_sql -DNSHostName gmsa_sql.corp.example `
        -PrincipalsAllowedToRetrieveManagedPassword 'SQL Servers' `
        -ServicePrincipalNames 'MSSQLSvc/sql01.corp.example:1433' -WhatIf
    
  4. Where a gMSA is not possible, give the account a random password of 25 or more characters, allow only AES, and reset the password afterwards so that AES keys exist:

    Set-ADUser svc_sql -KerberosEncryptionType AES128, AES256 -WhatIf
    
  5. Take service accounts out of tier-0 groups. A service rarely needs Domain Admins; grant the specific rights it needs instead.

  6. Detect attempts: on domain controllers, alert on event 4769 for user-owned SPNs with ticket encryption type 0x17 (RC4).

Verify

ad-doctor scan dc01.corp.example --ldaps -u 'CORP\auditor' --only AD-KRB-003

To accept the risk instead, add an entry to the file passed with --accept (see accepted risks):

[[accept]]
check = "AD-KRB-003"
objects = ["..."]        # optional; without it the whole check is accepted
reason = "..."
expires = 2027-03-31

References

Microsoft and vendors

Research

MITRE ATT&CK

Available since 0.1.0.