KerberLoss (CVE-2026-25177): Invisible Unicode in Service Principal Names for DoS, Kerberos Downgrade, and SPN-Jacking
Executive Summary
- KerberLoss (CVE-2026-25177), discovered and disclosed by Semperis, abuses a mismatch in how Active Directory handles Service Principal Names (SPNs). One check sees a name as unique, another lookup treats it as identical to an existing name.
- The lever is an invisible Unicode character, such as a zero-width non-joiner (
U+200C) or zero-width space (U+200B), placed inside theservicePrincipalNamevalue. It is not visible in the console and it slips past SPN uniqueness, yet LDAP resolution for the clean name still returns the object that carries the hidden character. - With write access to an account's
servicePrincipalName, an attacker can do three things: deny access to a HOST-mapped service, quietly downgrade authentication from Kerberos to NTLM, or hijack a service for a constrained delegation (S4U) attack. - This bypasses the SPN uniqueness verification that Microsoft added in 2021. Microsoft patched KerberLoss in March 2026.
- The malicious SPN is durable evidence. It sits in the directory as a duplicate or as a value with an odd character in it, and both are straightforward to hunt for.
The Vulnerability
A Service Principal Name is how Kerberos knows which account owns a service. When a client wants to talk to cifs/serverb.demo.lab, the KDC finds the account that holds that SPN, and encrypts the service ticket with that account's key. For this to be safe, an SPN has to belong to exactly one account. That is why Microsoft's 2021 hardening (CVE-2021-42282) added uniqueness verification for UPNs, SPNs, and SPN aliases, so two accounts could no longer claim the same name.
KerberLoss defeats that guarantee by making two components of Active Directory disagree about what "the same name" means.
- The uniqueness check compares the full string, byte for byte.
cifs/serverbandcifs/serverbplus a hiddenU+200Care different strings, so the write is allowed. - The LDAP lookup that Kerberos uses to resolve the SPN applies the directory's string matching and ordering rules, and certain invisible characters are, in the words of the research, "completely ignored by the DC." So a query for
cifs/serverbmatches the object that actually storescifs/serverb{U+200C}.
The result is a name that is unique enough to be written but identical enough to be resolved. The attacker registers a name that the directory will hand out on behalf of the wrong account, and nobody sees the difference in the management tools.
Three Ways to Abuse It
All three start from the same primitive, a duplicate or shadowing SPN created with a hidden character. They differ in what the attacker attaches it to.
1. Denial of service against a service. The attacker, holding write access on some account, adds cifs/serverb{invisible} to a machine they influence, say ServerC. Now when clients ask for cifs/serverb, the KDC resolves the shadowed name and encrypts the ticket with ServerC's key. The real ServerB cannot decrypt a ticket meant for another machine, so it rejects it with KRB_AP_ERR_MODIFIED. Access to that service stays broken until the malicious SPN is found and removed. Because the offending value is invisible in the console, the fault is hard to diagnose.
2. Kerberos-to-NTLM downgrade. Here the attacker creates a duplicate of an explicit SPN, for example HOST/server{U+200C}, on a second account. Now two accounts appear to hold the same host name. When a client requests a ticket, the KDC finds the conflict and returns KDC_ERR_S_PRINCIPAL_UNKNOWN. The client does what Windows clients do when Kerberos cannot produce a ticket: it falls back to NTLM. Access still works, so the user notices nothing, but the session is now on the weaker protocol, which is exactly the position an NTLM relay or offline-cracking attacker wants. Only a packet capture reveals that Kerberos was skipped.
3. SPN-jacking for constrained delegation. The most serious use turns the trick into privilege escalation. In a constrained delegation chain, a front-end service is allowed to impersonate users to a specific back-end SPN. Normally the attacker would need write access on the intermediate service to point it at a target. With KerberLoss they do not. They shadow the target SPN (cifs/serverb{invisible}) onto an account they control, so tickets for cifs/serverb are encrypted with their key. Running the standard S4U2Self and S4U2Proxy flow, they obtain a privileged service ticket for the delegated target without ever holding write access on the intermediate, hijacking the service from the side.
Prerequisites and Scope
- Write access to an account's
servicePrincipalName. That can be an explicit write-property right on the SPN attribute, or a broader right such as generic write or full control that includes it. It applies to user and computer accounts alike. No Domain Admin access is required. - On-premises Active Directory, forest-wide. This is a directory and Kerberos issue, with no AD CS involvement.
- The impact ranges from nuisance (a broken service) to serious (a silent downgrade, or a delegation-based escalation), depending on what the attacker targets.
Detection and Hunting
The malicious SPN has to exist in the directory for the attack to work, which makes this very findable. We reproduced the state in a lab: an account svc-sql-x was given the SPN MSSQLSvc/sql01.fslab.local:1433 with a trailing zero-width non-joiner (U+200C), shadowing the real svc-sql. Both a Server 2019 and a Server 2025 Domain Controller accepted the hidden-character write, and the checks below found it.
Catch the change (event log). Monitor Event ID 5136 for servicePrincipalName additions that conflict with an SPN another account already holds, and for SPN values that contain unusual or non-printable characters when your logging preserves them. Semperis DSP includes indicators for objects that carry hidden Unicode characters and for suspicious duplicates created with them.
Hunt it with PowerShell (no extra tooling). This walks every SPN and flags any character outside printable ASCII, printing the code points so the hidden one is obvious:
$sp = Get-ADObject -LDAPFilter '(servicePrincipalName=*)' -Properties servicePrincipalName, sAMAccountName
foreach ($o in $sp) { foreach ($s in $o.servicePrincipalName) {
if ($s -cmatch '[^ -~]') {
$cp = ($s.ToCharArray() | ForEach-Object { 'U+{0:X4}' -f [int]$_ }) -join ' '
"{0} {1}`n {2}" -f $o.sAMAccountName, $s, $cp
}
} }
In the lab it returns the shadow account, with the trailing U+200C laid bare among the printable characters:
svc-sql-x MSSQLSvc/sql01.fslab.local:1433
U+004D U+0053 U+0053 U+0051 U+004C U+0053 U+0076 U+0063 ... U+0031 U+0034 U+0033 U+0033 U+200C
Find it in directory state (graph). If you run Forestall ISPM, it stores every account's SPNs verbatim, so the hidden character is one query away. Run these in the graph view, or directly against Neo4j.
The reliable signal is an SPN that carries a zero-width or other invisible Unicode character. A well-formed SPN never does, so this is close to false-positive free:
// SPN values carrying invisible Unicode (ZWSP, ZWNJ, ZWJ, word joiner, BOM)
MATCH (n) WHERE (n:User OR n:Computer) AND n.ServicePrincipalName IS NOT NULL
UNWIND n.ServicePrincipalName AS spn
WITH n, spn WHERE spn =~ '.*[\\x{200B}\\x{200C}\\x{200D}\\x{2060}\\x{FEFF}].*'
RETURN n.SAMAccountName AS account, spn AS suspiciousSpn
To catch a shadow even if it uses a character outside that list, group SPNs that are identical after stripping the invisibles but whose raw strings still differ. That difference is the tell, two entries that look the same but are not byte for byte:
// Same SPN after stripping invisibles, but the raw strings differ (a hidden-character shadow)
MATCH (n) WHERE (n:User OR n:Computer) AND n.ServicePrincipalName IS NOT NULL
UNWIND n.ServicePrincipalName AS raw
WITH n.SAMAccountName AS account, raw,
reduce(x = raw, c IN ['\u200B','\u200C','\u200D','\u2060','\uFEFF'] | replace(x, c, '')) AS clean
WITH toLower(clean) AS spn, collect(DISTINCT raw) AS rawForms, collect(DISTINCT account) AS accounts
WHERE size(accounts) > 1 AND size(rawForms) > 1
RETURN spn, rawForms, accounts
A word on false positives. Do not use a plain "same SPN on two accounts" scan as a KerberLoss detector on its own. Real directories carry legitimate duplicate SPNs: replication and directory SPNs shared between Domain Controllers, a cloned or staged server that kept its original names, or a service account misconfiguration. In our own lab that plain scan returned a handful of those alongside the planted pair. The two queries above avoid the noise by keying on what KerberLoss actually leaves behind, an invisible character in the first, and a raw-versus-normalized mismatch in the second. A byte-identical duplicate is a hygiene problem to clean up, not this attack.
The precondition is visible as well. KerberLoss needs write access to an SPN, and Forestall ISPM maps write access over accounts as attack-path exposure, so the accounts that could seed this attack already appear in your dangerous-permission findings.
Mitigation
- Install the March 2026 security update on Domain Controllers. This is the fix. It hardens SPN validation so that the invisible-character bypass no longer works. Confirm the exact KB for each Windows Server version in the Microsoft Security Update Guide entry for CVE-2026-25177.
- Restrict who can write SPNs. Review and tighten write access to
servicePrincipalName, especially broadGenericWrite,GenericAll, or write-property rights held by unprivileged accounts over servers and service accounts. - Clean up duplicate SPNs. Use the query above to find and resolve accounts that share an SPN. Duplicates are a problem even without an attacker.
- Watch for the indicators. Alert on Event 5136 for conflicting or oddly encoded SPN additions, and run the graph queries on a schedule.
Conclusion
The root cause is narrow. A uniqueness check that compares raw bytes and an LDAP lookup that ignores certain invisible characters do not agree on what "the same SPN" means. An attacker who can write an SPN turns that disagreement into a broken service, a silent drop to NTLM, or a stolen delegation.
The March 2026 update closes the gap and is the real fix. Until it is deployed, the attack cannot hide its own evidence: the malicious SPN sits in the directory as a duplicate or as a value with a zero-width character, and both are easy to find in your event logs or in a graph of your directory.
References
See your identity exposure clearly.
Start with a 1-day Proof of Value in your own environment.