Skip to content

AD-DEL-005: Constrained delegation to SPNs no account holds

low medium high   Delegation   MITRE ATT&CK T1558, T1134   Reads: LDAP

msDS-AllowedToDelegateTo names an SPN no account holds, so someone who controls the delegating account and can write SPNs can register it and impersonate users to an account they control.

Also known as: ghost-spn, spn-jacking, orphaned-spn

Why it matters

Decommissioned servers leave their names behind in delegation settings. The KDC finds the target of a delegated ticket by looking for the account that holds the SPN, so an SPN that nobody holds can be claimed.

This is SPN-jacking. It needs two things: control of the delegating account, and the right to write servicePrincipalName on any account, which may be a low-value one. The attacker registers the dead SPN there, and the delegating account now gets tickets as any user to an account the attacker controls. With protocol transition no user has to authenticate first, and without it the attacker can still chain resource-based delegation. Taken alone the leftover grants nothing, so it is a condition for an attack, not a proven path.

How ad-doctor detects it

Each msDS-AllowedToDelegateTo value of an enabled account is judged from the snapshot:

  1. Any account, enabled or not, holds the exact SPN: fine.
  2. The service class maps to another one through sPNMappings, and an account holds the mapped SPN: fine. On a default forest cifs/fs01.corp.example, http/... and about 50 other classes are answered by HOST/fs01.corp.example. Custom mappings count. Only the class changes, so http/fs01.corp.example:8080 needs HOST/fs01.corp.example:8080.
  3. The host is known, as a computer or managed service account, or as the host of any SPN: (no account holds it, the host exists).
  4. The host is short, or directly under the scanned domain's name, and unknown: (no computer object).
  5. A host below the scanned domain's name, or an address: (not in this domain's snapshot, review), low. It may be a child domain.
  6. Any other host: (another domain, this delegation cannot reach it). Delegation set on the account works in one domain only, so the entry cannot work, and anyone able to write SPNs here can still claim the name.

Steps 3, 4 and 6 are medium, or high with , protocol transition added to the text.

krbtgt targets are AD-DEL-004's. No DNS query is made: the answer comes from directory objects only.

If sPNMappings cannot be read, step 3 is left out, because a mapping could explain the target, and a note says so. The other steps do not need it. An older snapshot behaves the same.

Attributes and probes read
  • servicePrincipalName
  • msDS-AllowedToDelegateTo
  • userAccountControl
  • dNSHostName
  • sPNMappings (on CN=Directory Service)

Severity

Set by the worst target, and by the delegating account, not by the host: the attack is a write on an account the attacker holds, so a free SPN on a live host can be claimed like one on a dead host. High when the account has protocol transition, so it can impersonate anyone without their credentials. Medium without it: that takes one more step, not a limit, because a forwardable ticket can come from resource-based delegation. Low for targets that need a review.

False positives and limits

  • Objects the scan cannot see. A computer in an OU the scanning account cannot list looks absent. Scan with an account that can list the whole domain before removing anything.
  • Aliases without an SPN. A DNS alias works for delegation only if some account holds an SPN with that name, so an alias with no SPN is reported, and rightly: the KDC cannot find it.
  • Hosts in a subdomain of the DNS name that belong to this AD domain are reported as review, not as ghosts.
  • An SPN that only a port or instance name keeps from matching is reported on purpose: the KDC looks for that exact name. This reading comes from [MS-DRSR], not from a KDC.

Fix

  1. List what each account may delegate to:

    Get-ADObject -LDAPFilter '(msDS-AllowedToDelegateTo=*)' -Properties msDS-AllowedToDelegateTo |
        Select-Object Name, msDS-AllowedToDelegateTo
    
  2. Remove dead targets, and entries for other domains, which this kind of delegation cannot reach, on the Delegation tab of the account or with a command that you review first:

    Set-ADUser svc_app -Remove @{ 'msDS-AllowedToDelegateTo' = 'cifs/oldfs.corp.example' } -WhatIf
    
  3. If the service moved, point the entry at the new host, or register the SPN on the account that runs the service with setspn -S.

  4. Review who can write servicePrincipalName, and move to resource-based delegation where you can. It is set on the target, so there is no name left to claim.

Verify

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

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

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

References

Microsoft and vendors

Specifications

Research

MITRE ATT&CK

Available since 0.1.4.