Active Directory • Identity Security

The Ghost SPN Attack: Kerberoasting That Erases Its Own Audit Trail

Dark cyberpunk illustration of a fractured translucent key dissolving over a glowing network lattice, one node flickering out mid-connection.

An analyst reviewing Active Directory logs this morning will see nothing wrong. A standard user account briefly holds a service principal name, a Kerberos ticket gets requested against it, and a few minutes later the attribute is gone and the account looks exactly like it did before anyone touched it. No lockout. No failed logon. No spike in ticket requests to trip a volume-based alert. The only artifact left behind is a Kerberos ticket already sitting on an attacker's machine, encrypted with a password hash that is being cracked offline, away from anything you monitor.

Trellix researchers documented this technique, which they named Ghost SPN, in a pair of posts earlier this year: "When SPNs Go Rogue" in February and "Now You See It, Now You Don't" in May. It has been working back through security trade coverage since late August, and for good reason. Seven months on, the Kerberoasting detections most environments run still do not catch it, because those detections were built to catch something else.

Here is who this applies to. If you run on-premises Active Directory and any group other than Domain Admins can modify user object attributes, a help desk team resetting passwords, a Tier 2 support group, an identity-management tool running under a service account, you already have the access pattern Ghost SPN abuses. That covers most organizations with an IT department larger than one person. If your environment is Entra ID only with no domain controllers, this specific technique does not apply to you; SPNs and Kerberos ticket-granting are an on-premises AD construct. Keep reading if you still run a domain. Skip the rest if you genuinely do not.

The myth: Kerberoasting is already a solved detection problem

Kerberoasting has been public knowledge since 2014, and most mature security programs believe they have it covered. The standard defense looks like this: a SIEM rule watching Windows Event ID 4769 for RC4-encrypted (etype 0x17) ticket-granting-service requests, an alert threshold on how many distinct service tickets one account requests in a short window, maybe a canary service account seeded to trip on unauthorized ticket requests. Tools attackers actually use for classic Kerberoasting, Impacket's GetUserSPNs.py, Rubeus, work by enumerating every account in the domain that already carries an SPN and requesting tickets for a batch of them at once. That burst is loud. It is also the only shape of Kerberoasting most detections are tuned to see.

The assumption underneath that tuning is that an attacker has to find existing SPN-bearing accounts to target, because service accounts are the ones with SPNs. That assumption held until an attacker with write access to one AD attribute decided it did not have to.

The reality: Ghost SPN erases the thing your detection watches for

Ghost SPN does not enumerate anything. An attacker who already holds GenericAll, GenericWrite, or a direct WriteProperty grant over a target's servicePrincipalName attribute, permissions delegated to help desk and identity-management accounts far more often than they get audited, picks one ordinary employee's account and assigns it a fake SPN, something like http/internal-app01. That single write converts a normal user account into a valid Kerberoasting target with no actual service behind it.

The attacker then requests a ticket-granting-service ticket for that SPN. The KDC issues it, encrypted with the target account's password hash, exactly as Kerberos is designed to do. One request. No enumeration, no burst, nothing that trips a volume threshold. Trellix's research describes the operator then clearing the attribute and handing the account back to its original state before anyone reviewing it would have reason to look.

Two Windows Event 5136 entries mark the change: one recording the SPN attribute being written, a second recording it being deleted, usually minutes apart. Neither fires an alert on its own in most environments, because 5136 logs routine object changes constantly and almost nobody scopes SACL auditing narrowly enough to flag a single attribute on a single object class. The ticket request itself is a single 4769 event indistinguishable from a legitimate service authenticating, unless you correlate it against the attribute change that made the account ticket-able in the first place. That correlation is the entire gap. Most Kerberoasting detections were built to catch volume. Ghost SPN was built around never generating any.

Where this actually starts: the ACL you already delegated

Ghost SPN needs a foothold, not a vulnerability. Nobody is patching this; there is nothing to patch. It is a permissions and monitoring gap, and the write access it depends on almost always traces back to one of three patterns that exist in most AD environments for legitimate operational reasons and rarely get revisited.

Three ACL patterns worth auditing this week

  • Over-scoped help desk delegation. Running "reset a user's password" through the Delegation of Control wizard, then later hand-editing the ACL to fix an edge case, frequently lands on GenericAll instead of the narrower Reset Password extended right. GenericAll covers every attribute on the object, SPN included.
  • Identity-management and provisioning service accounts. HR-to-AD sync tools, ticketing-system automations, and self-service portals often run under a service account granted broad write access across an OU so nobody has to revisit permissions when a new attribute needs updating. That account is now worth compromising in its own right.
  • Legacy nested group membership. A group created years ago for a project that needed write access to a sub-OU, later nested into a broader support group during a reorg. Nobody remembers the original grant until an audit surfaces it.

Any of these, held by an attacker who has already landed on a workstation through phished or reused credentials, is enough. Ghost SPN is a second-stage technique. It assumes initial access already exists, which is the ordinary case in most intrusions this year, not the exception.

Audit which non-administrative principals can write to servicePrincipalName before assuming this does not apply to you. The following pulls every account or group holding GenericAll, GenericWrite, or a direct WriteProperty grant on user objects, excluding the built-in administrative groups you would expect to have it:

# PowerShell — enumerate non-admin principals with write access to user objects
Import-Module ActiveDirectory
$domainDN = (Get-ADDomain).DistinguishedName

Get-ADUser -Filter * -SearchBase $domainDN -Properties nTSecurityDescriptor |
  ForEach-Object {
    $user = $_
    $user.nTSecurityDescriptor.Access |
      Where-Object {
        $_.ActiveDirectoryRights -match 'GenericAll|GenericWrite|WriteProperty' -and
        $_.IdentityReference -notmatch 'SYSTEM|Domain Admins|Enterprise Admins|Administrators|SELF'
      } |
      ForEach-Object {
        [PSCustomObject]@{
          Account = $user.SamAccountName
          Grantee = $_.IdentityReference
          Rights  = $_.ActiveDirectoryRights
        }
      }
  } | Sort-Object Grantee | Format-Table -AutoSize

Run it against a handful of OUs first in a large domain; the unrestricted scan is slow. Anything returned that is not a documented break-glass admin group or a known service account is worth a conversation before it becomes an incident.

Detecting the six-minute window

Once the ACL exposure is understood, detection has to shift from watching volume to watching sequence: an SPN written to a non-service account, a ticket requested for that account, and the SPN removed again, all inside a short window. That correlation is what a canary account and a raw 4769 rule both miss on their own.

If you forward Windows security events to Microsoft Sentinel, the following joins the paired directory-service-change events against a ticket request that falls between them. It requires directory service changes auditing enabled on your domain controllers with servicePrincipalName included in the audited attribute list, a setting most environments have never turned on.

// KQL — Sentinel: SPN added, ticket requested, SPN removed, inside 15 minutes
let spnAdds = SecurityEvent
| where EventID == 5136 and ObjectClass == "user"
    and AttributeLDAPDisplayName == "servicePrincipalName"
    and OperationType == "%%14674" // Value Added
| project AddTime = TimeGenerated, TargetObject = ObjectDN, AddedBy = SubjectUserName;
let spnDeletes = SecurityEvent
| where EventID == 5136 and ObjectClass == "user"
    and AttributeLDAPDisplayName == "servicePrincipalName"
    and OperationType == "%%14675" // Value Deleted
| project DeleteTime = TimeGenerated, TargetObject = ObjectDN, DeletedBy = SubjectUserName;
let ticketReqs = SecurityEvent
| where EventID == 4769
| project TicketTime = TimeGenerated,
    TargetAccount = tostring(split(TargetUserName, "@")[0]);
spnAdds
| join kind=inner spnDeletes on TargetObject
| where DeleteTime - AddTime between (0min .. 15min)
| extend TargetUserName = tostring(split(TargetObject, ",")[0])
| join kind=inner ticketReqs on $left.TargetUserName == $right.TargetAccount
| where TicketTime between (AddTime .. DeleteTime)
| project AddTime, TicketTime, DeleteTime, TargetObject, AddedBy, DeletedBy

The result set should be close to empty in a healthy environment; legitimate SPN registrations are rare, deliberate, and do not get reversed minutes later. Anything this query returns deserves a ticket, not a shrug.

Pull the ACL report before you pull the Kerberoasting dashboard

Most AD hardening reviews start with the dashboard: which accounts have SPNs today, which of those are cracked-password risks, whether RC4 is disabled anywhere. Ghost SPN makes that starting point incomplete, because the account that matters this afternoon might not have an SPN yet. Start instead with who can write to one, then close the gaps the audit above surfaces:

  • Replace GenericAll delegations with the narrow right. Password reset delegation needs the Reset Password extended right and control over lockoutTime, nothing broader. Rebuild any help desk delegation that was assembled through trial and error rather than the documented extended rights.
  • Move real service accounts to gMSA. Group Managed Service Accounts rotate their own passwords on a schedule nobody manages by hand, removing the static, crackable password hash Kerberoasting depends on, whether the SPN was assigned legitimately or not.
  • Require AES, retire RC4. Set msDS-SupportedEncryptionTypes to AES-only on service and privileged accounts. An AES-encrypted ticket is still crackable against a weak password, but this removes RC4 as the detection shortcut most SIEM rules still lean on alone.
  • Scope directory-service auditing to identity-relevant attributes. Most domains generate 5136 for everything or for nothing. Narrow the SACL to servicePrincipalName, msDS-AllowedToDelegateTo, and sIDHistory specifically, so the volume stays low enough that someone actually reads the alerts.

Run the audit script above this week, account for every non-administrative principal it returns, and treat any you cannot immediately explain as a finding rather than a formality. Identity infrastructure accumulates access the way a filing cabinet accumulates folders: nobody deletes anything, everybody adds. Ghost SPN is what happens once an attacker reads what accumulated.

Need help hardening your identity infrastructure?

We assess Active Directory and Entra ID environments for the misconfigurations attackers actually exploit. Book a session to discuss your environment.