core-jmp core-jmpdeath of core jump

Identity Crisis: Kerberos downgrade, DoS, and full domain takeover via KerberLoss and ResetNightmare

Active Directory stores Unicode characters its own LDAP server cannot search for. Semperis researcher Shai Laron turned that gap into KerberLoss (CVE-2026-25177) - a forest-wide DoS against HOST-mapped services, an improved SPN-jacking primitive, and a forced Kerberos-to-NTLM downgrade - then found ResetNightmare (CVE-2026-27912), where the Kerberos Change Password protocol skips the TGS exchange that validates PAC_REQUESTOR_SID, letting generic Write on one object reset a Domain Admin password.

oxfemale August 10, 2026 31 min read 88 reads
Export PDF
Identity Crisis: Kerberos downgrade, DoS, and full domain takeover via KerberLoss and ResetNightmare
Original text: “Identity Crisis: Novel Vulnerabilities Leading to Kerberos Downgrade, DoS, and Full Domain Takeover”Shai Laron, Security Researcher, Semperis (August 5, 2026). Figures, protocol structures and the sPNMappings list below are reproduced verbatim with attribution captions.

Executive Summary

Active Directory is still the control plane of most enterprises, and Domain Admin is still the prize. Defenders spend enormous effort keeping privileged credentials out of attacker hands — but credentials are only half the story. If a domain controller can be made to mistake one identity for another, the attacker never needs to steal anything. Semperis researcher Shai Laron started from a curiosity — invisible Unicode characters in AD object names, presented by Yossi Sassi as a persistence trick — and ended up with two new Microsoft-assigned vulnerabilities that turn naming confusion into real impact: KerberLoss (CVE-2026-25177) and ResetNightmare (CVE-2026-27912).

KerberLoss exploits a gap between what Active Directory’s uniqueness checks compare and what the LDAP server can actually filter on: a set of Unicode characters that the DC silently ignores. Slipping one of those characters into a Service Principal Name defeats the SPN and SPN-alias uniqueness verification introduced by the CVE-2021-42282 patch, which yields a forest-wide denial of service against any HOST-mapped service, an improved variant of Elad Shamir’s SPN-jacking that no longer needs WriteSPN on the intermediate host, and a reliable way to force any service in the forest to fall back from Kerberos to NTLM. ResetNightmare is the more serious of the two: by setting a low-privileged account’s UPN to a Domain Admin’s SamAccountName and requesting a TGT with the NT-ENTERPRISE name type, an attacker gets a ticket bearing the admin’s name — and because the Kerberos Change Password protocol goes straight from AS-REQ to AP-REQ, it never passes through the TGS exchange where the PAC_REQUESTOR_SID validation from the CVE-2021-42287 patch happens. The result is an instant password reset of a Domain Admin from generic Write permissions on a single object. Microsoft patched KerberLoss in March 2026 and ResetNightmare in April 2026.

The research began with a talk. Yossi Sassi, speaking about Active Directory persistence, demonstrated something the author had not seen before: “invisible” Unicode characters can be embedded in object attributes, producing accounts that look identical to legitimate ones. As a persistence technique it is effective precisely because it wastes an investigator’s time — two objects on screen, indistinguishable, one of them planted.

That raised three questions worth chasing:

  • Does Active Directory accept “invisible” characters other than the handful already known?
  • How could abuse of those characters be detected efficiently, at scale?
  • Do hidden characters have uses beyond persistence?

The answer to the third question turned out to be the interesting one.

Initial research: Unicode and Active Directory

The questions led into a rabbit hole about Unicode and server-side LDAP processing. The first step was mechanical: sweep the web for Unicode code points that render as nothing, and collect them into a list. The characters of interest were specifically those that stay invisible inside object names, since that is where the confusion is most useful.

Testing started with a sample account named UniqueUser. Creating a second account with the same name fails, exactly as you would expect (Figure 1).

Creating duplicate users is blocked by default
Figure 1. Creating duplicate users is blocked by default. Source: original article.

Insert one “invisible” character, however, and an apparently identical account is created without complaint (Figure 2).

By adding a hidden Unicode character to the string, the command successfully runs
Figure 2. By adding a hidden Unicode character to the string, the command successfully runs. Source: original article.

The PowerShell console renders the result with a strange gap before “ser” — a small tell, and one that only shows up in a terminal. Open the same two accounts in the graphical AD management tools and they are indistinguishable (Figure 3).

Different users seem identical, due to hidden characters
Figure 3. Different users seem identical, due to hidden characters. Source: original article.

With the method established, the whole candidate list was iterated: one user account per character. Some of the characters turned out not to be invisible at all once Active Directory got hold of them (Figure 4).

Some Unicode characters were visible instead of hidden
Figure 4. Some Unicode characters were visible instead of hidden. Source: original article.

Pruning those left a much cleaner directory (Figure 5).

My directory after cleaning up users with visible characters
Figure 5. My directory after cleaning up users with visible characters. Source: original article.

Two different kinds of failure showed up during the sweep. Some characters the DC simply refused. Others were skipped because the DC insisted the username already existed — which, in hindsight, was the entire research foreshadowing itself. To get around the uniqueness constraint and test as many characters as possible, a second batch of accounts was created, each name carrying one candidate character sandwiched between two dashes (Figure 6).

Creating distinct users for each hidden Unicode character
Figure 6. Creating distinct users for each hidden Unicode character. Source: original article.

The end state was a working list of 385 invisible characters — question one answered. Time to look at detection.

A different detection approach

Sassi’s original talk shipped a detection tool, and it is worth understanding how it works. The script takes a straightforward client-side approach:

  • Create a dictionary of 29 “invisible” characters.
  • Get all properties of all objects in the domain.
  • Iterate through each attribute.
  • Convert the attribute to an array of characters.
  • Retrieve the hex value of each character and compare it against the dictionary.

That is comprehensive, and comprehensiveness has a cost: pulling every attribute of every object and walking it character by character is not something you want running routinely against a production forest. The natural question is whether the filtering can be pushed into the LDAP request itself — evaluated server-side rather than reconstructed on the client.

The first test was the simplest one possible: can the DC process these characters as-is? Character 0x200B was converted to its invisible string form, then pasted straight into a PowerShell LDAP filter (Figure 7).

A query with 0x200B pasted between Unique and User
Figure 7. A query with 0x200B pasted between “Unique” and “User”. Source: original article.

That worked: the filter returned exactly the expected object. Then things got strange. The same test with 0x200C — another invisible character — returned a result even though no account named Unique{0x200C}User existed in the directory at that point, because that character had never survived the uniqueness test in the first place (Figure 8).

A query with 0x200C pasted between Unique and User
Figure 8. A query with 0x200C pasted between “Unique” and “User”. Source: original article.

Look closely at that result and the odd console spacing from the earlier screenshots is gone. Verifying by SID confirmed the suspicion: the object returned was the plain UniqueUser, with no added characters at all. The Unicode character in the filter had been discarded — by PowerShell, by the DC, or somewhere in between.

If that theory held, then filtering for any object containing 0x200C should return the entire directory. Converting the Unicode value inside the console for readability, that is precisely what happened (Figure 9).

Looking for a SamAccountName that contains 0x200C returns all identities in the domain
Figure 9. Looking for a SamAccountName that contains 0x200C returns all identities in the domain. Source: original article.

So the LDAP server’s behaviour was inconsistent: some characters were evaluated, others quietly dropped. To rule out the filter string itself as the culprit, the next stop was RFC 4515, which states:

The string representation of an LDAP search filter is a string of UTF-8-encoded Unicode characters

RFC 4515

The RFC also provides worked examples, which suggested that any Unicode character should be convertible and queryable regardless of whether it is printable. Following that specification, a helper function was written to take a Unicode hex value (for example 0x200B) and return its LDAP-filter-compatible escaped string (Figure 10).

The Convert-UnicodeToLdapUtf8 function, based on RFC 4515
Figure 10. The Convert-UnicodeToLdapUtf8 function, based on RFC 4515. Source: original article.

The function was validated against 0x200B, which had already been filtered successfully in the earlier tests (Figure 11).

Verifying that the function works by testing 0x200B
Figure 11. Verifying that the function works by testing 0x200B. Source: original article.

Then the problematic characters got the same treatment (Figure 12).

Testing the function with the problematic 0x200C
Figure 12. Testing the function with the problematic 0x200C. Source: original article.

Following the RFC did not help. Running the full sweep of 385 invisible characters through LDAP produced three distinct categories:

  • Filterable characters: only 106 out of 385.
  • Characters treated as whitespace: invisible in the GUI, but the filter returns every object name containing a space (e.g. “Domain Admins”, “Print Operators”).
  • Characters completely ignored by the DC: the filter returns all objects.

At that point the question had become personal. If the filter string could not be changed to fix this, what else could? The LDAP Extended Controls were the obvious remaining lever, and going through them, one stood out: LDAP_SERVER_SORT_OID. Its documented purpose is sort order, but Active Directory documentation notes in several places that the presence of the control also changes Unicode string comparison behaviour.

A function was written to attach LDAP_SERVER_SORT_OID and query LDAP with an arbitrary Ordering Rule OID. To test it, a scenario reported on a Microsoft forum was recreated with two users:

  • Shai
  • Shäi the 2nd

Note the difference between “a” (Unicode 0x0061) and “ä” (Unicode 0x00E4). Querying without the extended control (which defaults to US English) and then with the control plus a “Swedish” ordering rule produced genuinely different result sets, not merely a different sort order (Figure 13).

Different language ordering rules return different objects for the same filter
Figure 13. Different language “ordering rules” return different objects for the same filter. Source: original article.

The same behaviour held when “ä” was converted to its UTF-8-escaped form with Convert-UnicodeToLdapUtf8 (Figure 14).

Ordering rules still behave consistently when converting to UTF-8
Figure 14. Ordering rules still behave consistently when converting to UTF-8. Source: original article.

The hope was that some ordering rule would “filter the unfilterable”. A script iterated every available ordering rule against every problematic character. The conclusion was flat: no ordering rule makes the DC see any of them.

That closed the detection avenue with an unsolved problem left on the table — there are Unicode characters that Active Directory’s LDAP server ignores entirely. Which is a detection failure, and simultaneously an offensive primitive.

Changing hats

To recap the two facts that matter:

  • Some Unicode characters are not parsed properly by Active Directory and therefore appear invisible.
  • Some of those characters are unfilterable by LDAP, which means a filter for a “normal” value can return values that actually contain the hidden character.

Put together, that is a potential bypass of uniqueness constraints for any Unicode-based attribute. Initial testing against SamAccountName and cn showed Active Directory correctly blocking duplicate object creation even with unfilterable characters. Other attributes were not so lucky.

In 2021 Microsoft shipped a patch for CVE-2021-42282 that introduced three new uniqueness verification checks:

  • User Principal Name (UPN) uniqueness
  • Service Principal Name (SPN) uniqueness
  • SPN alias uniqueness

Each of those values must be unique across the entire forest, and all three checks are enabled by default through the forest-wide dSHeuristics attribute. Anyone below Domain Admin who tries to set a conflicting value receives a uniqueness error.

Unfilterable characters let low-privileged users walk straight past those checks. That is the first vulnerability — named KerberLoss for its various impacts, and assigned CVE-2026-25177 by Microsoft.

KerberLoss: Starting with SPNs

Understanding the impact requires a solid grasp of Service Principal Names, which are one of the more commonly misunderstood parts of Active Directory.

What are Service Principal Names?

An SPN is how Kerberos identifies a service instance. In Active Directory terms, a service is any resource that identities access and that generally requires authentication — SMB file shares (cifs), Remote Desktop (TERMSRV), LDAP, HTTP, and so on. Those are service classes.

Services of the same class can run on different hosts, and a single host can offer several classes. An SPN therefore carries both components, in the basic form:

<service class>/<host>

Note: SPNs may also include two additional, optional components, which are beyond the scope of this paper.

Those basics are widely known. Four points deserve emphasis, because the attacks below depend on all of them:

  • In the world of Active Directory and Kerberos, a service is hosted by an identity. It can be a computer account, a user account or a managed service account, but there must be an identity behind the service — this follows directly from the cryptographic model. Service tickets are encrypted with the secret belonging to the target service, meaning its identity.
  • Every identity object in Active Directory carries a servicePrincipalName attribute holding the list of that identity’s SPNs.
  • When a Kerberos service ticket is requested, the Key Distribution Center (KDC) uses the SPN from the request to identify the target service — that is, the identity that holds the SPN in question.
  • Kerberos tickets have a cleartext part and an encrypted part, and the SPN lives in the cleartext part. Because it is unencrypted, the SPN in a ticket can be edited, and tickets can be moved between different services of the same identity. What actually matters is which key encrypted the ticket.

What are SPN aliases?

Anyone who has managed an AD domain has seen that every computer carries a handful of default SPNs, including ones with the HOST service class (HOST/computer). If you have never had reason to look into them, they are confusing.

The Active Directory Configuration partition holds an attribute called sPNMappings, which maps SPNs to so-called SPN aliases. By default it contains a single value mapping the HOST alias to the following services:

alerter, appmgmt, cisvc, clipsrv, browser, dhcp, dnscache, replicator, eventlog, eventsystem, policyagent, oakley, dmserver, dns, mcsvc, fax, msiserver, ias, messenger, netlogon, netman, netdde, netddedsm, nmagent, plugplay, protectedstorage, rasman, rpclocator, rpc, rpcss, remoteaccess, rsvp, samss, scardsvr, scesrv, seclogon, scm, dcom, cifs, spooler, snmp, schedule, tapisrv, trksvr, trkwks, ups, time, wins, www, http, w3svc, iisadmin, msdtc

Every computer account gets a HOST class SPN when it joins the domain. When a user accesses a service that maps to HOSTcifs, http, and the rest of the list above — the KDC encrypts the service ticket with the key belonging to the account that holds the corresponding HOST SPN.

An interesting edge case

SPN alias uniqueness verification blocks the creation of conflicting mapped SPNs. If the forest contains a computer named Server holding HOST/Server, then assigning cifs/Server to a different server is refused — even though cifs/Server does not explicitly exist anywhere.

The vulnerability bypasses that check, but the detail that turns the bypass into an attack is the lookup order: the SPN lookup algorithm always searches for an explicit SPN first, and only falls back to the mapped alias when no explicit SPN is found. An attacker who can plant an explicit SPN therefore wins over the legitimate host’s implicit alias.

Demonstrating impact

Three scenarios follow, all in a simple environment with three domain-member servers:

  • ServerA
  • ServerB
  • ServerC

The attacker operates as a non-privileged user named NotAdmin.

Scenario #1: Denial-of-service to HOST-mapped services

Assume ServerB hosts an important SMB file share. Users accessing it request service tickets for cifs/SERVERB, and because ServerB holds HOST/SERVERB by default, the DC identifies it as the account holding the relevant encryption key. If NotAdmin has WriteSPN on ServerC, adding cifs/SERVERB to ServerC is refused by SPN alias uniqueness verification (Figure 15).

Non-domain admin users can't configure conflicting SPN aliases
Figure 15. Non-domain admin users can’t configure conflicting SPN aliases. Source: original article.

Insert an invisible, unfilterable character into the string and the same operation succeeds (Figure 16).

By using unfilterable characters, bypassing SPN alias uniqueness verification is possible
Figure 16. By using unfilterable characters, bypassing SPN alias uniqueness verification is possible. Source: original article.

As before, the character is not perfectly invisible in the PowerShell console — but it is invisible in dsa.msc, which is where an administrator would actually look (Figure 17).

dsa.msc displays a seemingly legitimate SPN
Figure 17. dsa.msc displays a seemingly legitimate SPN. Source: original article.

More importantly, an LDAP query for the cifs/SERVERB SPN now returns ServerC (Figure 18).

The LDAP server doesn't evaluate the unfilterable character when querying SPNs
Figure 18. The LDAP server doesn’t evaluate the unfilterable character when querying SPNs. Source: original article.

Because explicit SPNs take precedence, every user in the domain who now tries to reach ServerB over SMB receives a ticket encrypted with ServerC’s key. ServerB cannot decrypt it, and the session dies with KRB_AP_ERR_MODIFIED. To an ordinary user, that surfaces as one of several unhelpful messages:

  • The specified network name is no longer available.
  • The target account name is incorrect.
  • Cannot find path “path” because it does not exist.

Delete the fake SPN and access is restored immediately (Figure 19).

The malicious SPN blocks access to SMB; when removing the SPN, access is immediately restored
Figure 19. The malicious SPN blocks access to SMB; when removing the SPN, access is immediately restored. Source: original article.

The net effect: WriteSPN on any computer or user account in the forest is enough to deny service to any HOST-mapped service in the forest. Figure 20 shows the flow.

Flow of performing a DoS over arbitrary HOST-mapped services
Figure 20. Flow of performing a DoS over arbitrary HOST-mapped services. Source: original article.

Scenario #2: SPN-jacking

The second opportunity involves Kerberos constrained delegation, since classic constrained delegation is configured in terms of SPNs. The scenario here is a modified version of one from Elad Shamir’s write-up on SPN-jacking.

Note: This scenario relies on understanding Kerberos delegation attacks (i.e., “the full S4U attack”). An explanation of Kerberos delegation is beyond the scope of this paper.

Shamir’s setup: an attacker with admin access to ServerA wants admin access to ServerC. ServerA is configured for constrained delegation to cifs/ServerB, and the attacker holds WriteSPN on both ServerB and ServerC (Figure 21).

The lab setup from Shamir's paper
Figure 21. The lab setup from Shamir’s paper. Source: original article.

His approach to the uniqueness check was to use WriteSPN on both machines: temporarily strip HOST/SERVERB from ServerB, then add cifs/SERVERB to ServerC, run the full S4U attack through ServerA’s account to obtain a privileged service ticket to ServerC, and roll everything back afterwards.

KerberLoss plus explicit-SPN precedence removes the requirement for WriteSPN on the intermediate service entirely — a meaningful reduction in the attacker’s prerequisites. The lab was set up accordingly (Figure 22).

My lab setup; note that NotAdmin does not have the WriteSPN privilege on ServerB
Figure 22. My lab setup; note that NotAdmin does not have the WriteSPN privilege on ServerB. Source: original article.

Exactly as in the DoS case, unfilterable characters let the attacker give ServerC the cifs/SERVERB SPN, so tickets destined for cifs/SERVERB get encrypted with ServerC’s key (Figure 23).

Again, looking for the account that holds cifs/SERVERB returns ServerC
Figure 23. Again, looking for the account that holds cifs/SERVERB returns ServerC. Source: original article.

With that in place, the full S4U flow produces a privileged ticket to cifs/SERVERB (Figure 24).

Running the full S4U attack to get an administrator ticket for cifs/ServerB
Figure 24. Running the full S4U attack to get an administrator ticket for cifs/ServerB. Source: original article.

The test that proves the point: since the SPN sits in the unencrypted part of the ticket, it can simply be rewritten to cifs/ServerC and used against ServerC. That only works if the ticket really was encrypted with ServerC’s key rather than ServerB’s — and it works (Figure 25).

Successfully compromising ServerC
Figure 25. Successfully compromising ServerC. Source: original article.

Scenario #3: Authentication downgrade

Conflicting mapped SPNs cause a DoS. What happens with duplicate explicit SPNs is different, and arguably worse.

With a conflicting alias, the DC finds an SPN, issues a service ticket, and considers Kerberos to have worked. The denial of service happens later, on the resource, which cannot decrypt the ticket — hence KRB_AP_ERR_MODIFIED. With an exact duplicate of an explicit SPN, the DC finds two accounts holding the same SPN, cannot decide which key to use, and returns KDC_ERR_S_PRINCIPAL_UNKNOWN. Now the failure is signalled by the DC itself, Kerberos authentication is considered to have failed, and the client falls back to NTLM.

So WriteSPN on any computer or user account in the forest buys the attacker two things: a complete DoS of any HOST-mapped service, and the ability to force any service in the forest — HOST-mapped or not — onto NTLM only. If NTLM is disabled in the environment, the same trick becomes a denial of service instead.

From the user’s side nothing appears wrong; access continues to work (Figure 26, Figure 27).

After setting a duplicate HOST SPN, the access itself still works
Figure 26. After setting a duplicate HOST SPN, the access itself still works. Source: original article.
Wireshark shows the DC replying with KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, causing fallback to NTLM
Figure 27. Wireshark shows the DC replying with KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, causing fallback to NTLM. Source: original article.

This is the quiet one. A forced downgrade to NTLM does not break anything a user would report, and it re-opens every NTLM relay and coercion path the organisation thought it had closed by moving to Kerberos. Figure 28 illustrates the flow.

Flow of forcing arbitrary services to fall back from Kerberos to NTLM
Figure 28. Flow of forcing arbitrary services to fall back from Kerberos to NTLM. Source: original article.

ResetNightmare: What about UPNs?

DoS and downgrade are useful, but the goal was direct privilege escalation, which meant turning to User Principal Names. Since UPN uniqueness verification is governed by the same mechanism as SPN uniqueness verification, the assumption was that it could be bypassed the same way — and it could (Figure 29).

Duplicating DemoAdmin1's UPN using unfilterable characters
Figure 29. Duplicating DemoAdmin1’s UPN using unfilterable characters. Source: original article.

Uniqueness verification arrived alongside a series of fixes for vulnerabilities found by Andrew Bartlett — most famously the Dollar Ticket / noPac attack (CVE-2021-42287 + CVE-2021-42278), which let any user become a domain administrator on the spot. Because that attack also hinged on DC naming confusion, the hope was that KerberLoss might revive it or lead to something similar. Either way, the next step was understanding whether and how Kerberos uses UPNs at all.

Kerberos name types

In Kerberos a principal identifier consists of a Realm and a PrincipalName, and a PrincipalName is structured as follows:

PrincipalName   ::= SEQUENCE {
	   name-type       [0] Int32,
	   name-string     [1] SEQUENCE OF KerberosString
}

The name-string field carries the name itself, but a name alone is not sufficient to identify a principal. The name-type field states what kind of name it is — in practical terms, which attribute the DC consults first when locating the principal. RFC 4120 section 6.2 defines the possible values.

Active Directory normally identifies Kerberos clients with the NT-PRINCIPAL name type, which maps to the account’s SamAccountName. When issuing TGT requests it is also possible to use NT-ENTERPRISE, which locates accounts by their UserPrincipalName. The client name that ends up in the resulting ticket can be either the SamAccountName (NT-PRINCIPAL) or the UPN (NT-ENTERPRISE), usually governed by the Name-canonicalize flag.

Conveniently, mainstream Kerberos tooling — Rubeus and Impacket among them — already exposes the name type in TGT requests, so requesting tickets with NT-ENTERPRISE required no custom code.

The first attempt failed, though. Requesting a ticket with the duplicated UPN caused the DC to try authenticating the attacker as the privileged target, producing a pre-authentication failure (Figure 30).

Requesting a TGT with a duplicated UPN fails
Figure 30. Requesting a TGT with a duplicated UPN fails. Source: original article.

It did work if DemoAdmin1‘s UPN was removed first — but that requires write permissions over the target, which makes it useless as a privilege escalation primitive (Figure 31).

Getting a ticket for the UPN works only when removing the original UPN from the target user
Figure 31. Getting a ticket for the UPN works only when removing the original UPN from the target user. Source: original article.

The workaround is elegant and needs no unfilterable characters at all: instead of duplicating the target’s UPN, set your own UPN to the target’s SamAccountName. The strings are not identical, so uniqueness verification has no objection (Figure 32).

Setting the UPN of the unprivileged user to the Domain Admin's SamAccountName
Figure 32. Setting the UPN of the unprivileged user to the Domain Admin’s SamAccountName. Source: original article.

Requesting a ticket for DemoAdmin1 with UPNUser‘s password fails, as it should. Switch the name type to NT-ENTERPRISE, however, and out comes a ticket bearing DemoAdmin1‘s name (Figure 33).

By changing the name-type, we can get a ticket with the Domain Admin's name on it
Figure 33. By changing the name-type, we can get a ticket with the Domain Admin’s name on it. Source: original article.

That primitive — a ticket with an arbitrary username on it — was then thrown at every confusion idea available:

  • Normal AS-REQ to TGS-REQ
  • Modified Dollar Ticket attack flow
  • S4U2Self abuse
  • U2U Kerberos abuse
  • User logon DoS, as mentioned by Andrew Bartlett
  • Entra hard/soft match SyncJacking
  • Cross-domain UPN precedence

Every one of them failed. Depending on the tool and the method, the result was either an error or a ticket for the correct, unprivileged user. That consistency was itself a clue worth chasing.

The CVE-2021-42287 patch

The Dollar Ticket vulnerability rested on exactly this kind of naming confusion, and Microsoft’s fix added two features to Kerberos:

  • Returned TGTs always include a Privileged Attribute Certificate (PAC), even when the client explicitly asked for a ticket without one.
  • The PAC in TGTs now carries a field named PAC_REQUESTOR_SID, holding the SID of the client that requested the ticket.

PAC_REQUESTOR_SID is validated during the TGS exchange, which kills the original attack: deleting the account that requested the TGT no longer convinces the DC that the ticket belongs to the similarly named DC.

It is an excellent patch, and it is also precisely what was invalidating every UPN-confusion attempt above. The PAC always carried the attacker’s real SID, and the DC ignored the client name in the ticket entirely, treating the SID as the sole source of truth.

The natural next question — the one that turns this from a dead end into a domain takeover — is whether every Kerberos flow actually passes through a TGS exchange.

Breakthrough: (Ab)using the Kerberos Change Password protocol

By default, every Active Directory user can change their own password. One way to do it is Microsoft’s Kerberos Change Password and Set Password Protocol, which defines how password changes ride over Kerberos. The protocol is about as small as protocols get — the RFC is seven pages, with one request message and one reply message.

Microsoft’s terminology distinguishes “change password” (a user changing their own) from “set password” (an administrator setting someone else’s). The protocol listens on port 464 (kpasswd), with the request structure shown in Figure 34.

Kerberos Password Change request message
Figure 34: Kerberos Password Change request message. Source: original article.

Two parts of that message matter: the KRB-PRIV message and the AP-REQ structure.

KRB-PRIV is simply a mechanism for sending encrypted data, with room for custom payloads. The protocol uses that room to carry the new password (Figure 35).

Usage of KRB-PRIV in the Kerberos Change Password protocol
Figure 35. Usage of KRB-PRIV in the Kerberos Change Password protocol. Source: original article.

The AP-REQ structure is the interesting half. It contains a ticket plus an authenticator that proves legitimate possession of that ticket, since the authenticator is encrypted with the ticket’s session key (Figure 36).

The AP-REQ structure, containing a ticket and an authenticator
Figure 36. The AP-REQ structure, containing a ticket and an authenticator. Source: original article.

In the usual Kerberos flow, a TGS-REQ/TGS-REP yields a service ticket and its session key, and that response is what builds the AP-REQ sent to the service.

For the Change Password protocol, the ticket inside AP-REQ must be scoped to the kadmin/changepw SPN. But that SPN belongs to the krbtgt account — and, per the earlier point about SPNs living in the cleartext part of a ticket, all that really matters is which key encrypted it. A ticket to kadmin/changepw is therefore just a TGT with its sname rewritten.

Which means changing a user’s password requires nothing more than that user’s TGT, and the flow of a Kerberos password change looks like Figure 37.

Flow of a Kerberos change password operation
Figure 37. Flow of a Kerberos change password operation. Source: original article.

Note what is missing from that diagram. The flow goes straight from the TGT request to the AP-REQ, with no TGS-REQ in between — and the TGS-REQ is exactly where PAC_REQUESTOR_SID validation happens.

Given that a TGT with an arbitrary username was already obtainable via NT-ENTERPRISE, the question became simply whether the PAC_REQUESTOR_SID check had been implemented on this path too. The attack idea, resting on the default permission every AD user has to change their own password, runs as follows:

  1. The attacker controls a user named UPNUser with no special permissions beyond the ability to modify its own UPN value.
  2. The attacker sets that user’s UPN to the SamAccountName of the target — for example DemoAdmin1. No uniqueness bypass is needed: DemoAdmin1‘s real UPN is DemoAdmin1@demo.lab, so setting UPNUser‘s UPN to bare DemoAdmin1 is permitted (Figure 38).
Setting UPNUser's UPN value to the target's SamAccountName
Figure 38. Setting UPNUser’s UPN value to the target’s SamAccountName. Source: original article.
  1. The attacker requests a TGT for kadmin/changepw, specifying DemoAdmin1 as the username, NT-ENTERPRISE as the name type, and UPNUser‘s password.
  2. The DC returns a TGT that belongs to UPNUser according to PAC_REQUESTOR_SID in the PAC, but which carries the username DemoAdmin1 with the NT_ENTERPRISE type (Figure 39).
A TGT for DemoAdmin1 with the NT-ENTERPRISE name type is returned
Figure 39. A TGT for DemoAdmin1 with the NT-ENTERPRISE name type is returned. Source: original article.
  1. Used as-is, that ticket would reset UPNUser‘s own password. To escalate, the attacker changes or clears UPNUser‘s UPN value, so that no account in the directory holds the UPN printed on the ticket.
  2. Trying to use the ticket for a TGS-REQ after that change fails with KDC_ERR_TGT_REVOKED, thanks to the PAC_REQUESTOR_SID patch — impersonation is properly blocked. But using the same ticket to build a password change request works.
  3. The attacker then requests a fresh TGT for DemoAdmin1 with no name type specified. It succeeds, and the ticket’s name type is NT-PRINCIPAL, meaning it belongs to the real SamAccountName-backed DemoAdmin1 account (Figure 40).
A TGT for the real Domain Admin user
Figure 40. A TGT for the real Domain Admin user. Source: original article.

Success. The ability to write a single UPN value has become a full domain compromise.

This is ResetNightmare, assigned CVE-2026-27912. It allows complete domain takeover by an attacker holding generic Write permissions over any user or computer object in the domain, or who can create user or computer objects in the domain (MachineAccountQuota excluded). Figure 41 shows the flow end to end.

ResetNightmare attack flow
Figure 41. ResetNightmare attack flow. Source: original article.

There is one additional requirement: the target’s password must be sufficiently aged. Since the default Minimum password age in Active Directory is one day, that condition is met in practice almost always.

As a bonus — credited to Andrea Pierini — the vulnerability also composes with the Shadow Credentials technique, giving a stealthier path that abuses writable computer accounts instead.

An open-source community tool, also called ResetNightmare, implements the whole flow with parameters for customising execution. It is written in PowerShell and drives Rubeus.exe together with the PowerShell ActiveDirectory module (Figure 42); it is published on GitHub.

ResetNightmare.ps1 execution
Figure 42. ResetNightmare.ps1 execution. Source: original article.

Detecting and defending against KerberLoss and ResetNightmare

Semperis Directory Services Protector (DSP) customers can use the following new security indicators to detect several of the misconfigurations described above:

  • UPN or SPN uniqueness verification is disabled
  • Objects containing hidden Unicode characters
  • Suspicious duplicate objects using hidden Unicode characters
  • Non-privileged principal able to set a service principal name
  • Non-privileged principal able to set a user principal name

DSP additionally detects anomalous SPN and UPN modifications through two indicators of compromise:

  • A conflicting Service Principal Name has been added (CVE-2026-25177)
  • A User Principal Name matching another account’s SAM account name has been added (CVE-2026-27912)

Without that tooling, the most practical detection route is to configure SACLs auditing Active Directory object modifications. With the SACL in place, Security log event ID 5136 (“A directory service object was modified”) on domain controllers surfaces the changes that lead to either vulnerability’s impact.

For KerberLoss, the 5136 entry shows a ServicePrincipalName being added that conflicts with an existing one (Figure 43).

A Security log entry showing the addition of a conflicting SPN value
Figure 43. A Security log entry showing the addition of a conflicting SPN value. Source: original article.

For ResetNightmare, the 5136 entry shows a UserPrincipalName being added that corresponds to an existing SamAccountName (Figure 44).

A Security log entry showing the addition of a UPN value that mimics an existing SamAccountName
Figure 44. A Security log entry showing the addition of a UPN value that mimics an existing SamAccountName. Source: original article.

The best prevention is patching every domain controller. Microsoft patched KerberLoss (CVE-2026-25177) in March 2026 and ResetNightmare (CVE-2026-27912) in April 2026.

Beyond patching, stick to least privilege and monitor for abnormal additions of non-default permissions. Tighter delegation makes both vulnerabilities materially harder to reach.

Disclosure timeline

  • November 26, 2025: KerberLoss is discovered and reported to MSRC.
  • December 17, 2025: ResetNightmare is discovered and reported to MSRC.
  • January 9, 2026: MSRC confirms ResetNightmare works as reported.
  • January 17, 2026: MSRC confirms KerberLoss works as reported.
  • March 10, 2026: Microsoft patches KerberLoss (CVE-2026-25177) on Patch Tuesday as an Important Elevation of Privilege vulnerability.
  • April 14, 2026: Microsoft patches ResetNightmare (CVE-2026-27912) as an Important Elevation of Privilege vulnerability.

Acknowledgements

The original author credits the following researchers, whose work inspired various parts of this research:

  • Yossi Sassi (@Yossi_Sassi)
  • Andrew Bartlett
  • Elad Shamir (@elad_shamir)
  • Charlie Clark (@exploitph)
  • Will Schroeder (@harmj0y)
  • Andrea Pierini (@decoder_it)
  • Benjamin Delpy (@gentilkiwi)

Key Takeaways

  • Of 385 “invisible” Unicode characters accepted by Active Directory, only 106 can be filtered for over LDAP. The rest are either treated as whitespace or ignored outright — a gap between what the DC stores and what the DC can search.
  • That gap defeats the uniqueness verification introduced by the CVE-2021-42282 patch, because the uniqueness check is itself a search. This is KerberLoss (CVE-2026-25177).
  • SPN lookup prefers an explicit SPN over a HOST alias, so a planted cifs/TARGET beats the legitimate host’s implicit mapping — turning WriteSPN on any one object into a forest-wide DoS against HOST-mapped services.
  • A duplicated explicit SPN produces KDC_ERR_S_PRINCIPAL_UNKNOWN instead, so clients silently fall back to NTLM — an authentication downgrade users never notice and NTLM-relay operators very much do.
  • KerberLoss also improves Elad Shamir’s SPN-jacking by removing the need for WriteSPN on the intermediate service in a constrained-delegation chain.
  • ResetNightmare (CVE-2026-27912) needs no Unicode trickery at all: set your UPN to a Domain Admin’s SamAccountName, request a TGT with NT-ENTERPRISE, clear your UPN, and drive the Kerberos Change Password protocol to reset the admin’s password.
  • The root cause is architectural: the change-password flow goes AS-REQ → AP-REQ with no TGS exchange, and the TGS exchange is where PAC_REQUESTOR_SID is validated. A patch that covers one flow does not automatically cover another.

Defensive Recommendations

  • Patch every domain controller. KerberLoss was fixed in the March 2026 rollup and ResetNightmare in April 2026. A partially patched DC population leaves the attack reachable — a client only needs one vulnerable KDC to answer.
  • Enable SACL auditing for directory object modifications and alert on event ID 5136 where the modified attribute is servicePrincipalName or userPrincipalName. These are low-volume attributes in a healthy environment; the noise floor is manageable.
  • Alert specifically on a UPN whose value equals another account’s SamAccountName. That has no legitimate use and is the single clearest signature of ResetNightmare.
  • Verify that UPN and SPN uniqueness verification is still enabled in the forest-wide dSHeuristics attribute. Some legacy migration guides recommend disabling it; if it was turned off years ago and never restored, both classes of attack become trivial regardless of patch level.
  • Audit who holds WriteSPN, Write userPrincipalName, and generic Write on user and computer objects. Run BloodHound or an equivalent and treat generic Write on any object as a domain-takeover path until proven otherwise. Reduce MachineAccountQuota to 0 while you are there.
  • Hunt retroactively for hidden Unicode in object attributes. Because these characters cannot be filtered server-side, the sweep has to be client-side — pull servicePrincipalName, userPrincipalName, sAMAccountName and cn for all objects and compare each string against its ASCII-normalised form. Anything that differs deserves a look.
  • Treat unexplained KRB_AP_ERR_MODIFIED and KDC_ERR_S_PRINCIPAL_UNKNOWN as security events, not IT noise. The user-visible symptoms — “the specified network name is no longer available”, “the target account name is incorrect” — look like ordinary breakage, which is exactly what makes the DoS variant survivable for an attacker.
  • Monitor Kerberos-to-NTLM fallback rates per service. A service that suddenly stops authenticating with Kerberos and has not changed is worth investigating; on hosts where NTLM has already been disabled, the same trick manifests as an outage instead.

Conclusion

Both vulnerabilities come from the same underlying idea: a domain controller does not need to have its credentials stolen if it can be persuaded to mistake one identity for another. KerberLoss achieves that by exploiting the difference between the characters Active Directory will store and the characters its LDAP server can search for, turning a documented uniqueness guarantee into an assumption that does not hold. ResetNightmare achieves it by finding the one Kerberos flow that never visits the checkpoint where a previous patch installed its identity validation. Neither requires exotic access — write permission on a single object is the entire prerequisite for full domain compromise. Patch the DCs, audit who can write SPNs and UPNs, and treat naming confusion as a first-class attack class rather than a curiosity.

More resources

Disclaimer

This content is provided for educational and informational purposes only. It is intended to promote awareness and responsible remediation of security vulnerabilities that may exist on systems you own or are authorized to test. Unauthorized use of this information for malicious purposes, exploitation, or unlawful access is strictly prohibited. Semperis does not endorse or condone any illegal activity and disclaims any liability arising from misuse of the material. Additionally, Semperis does not guarantee the accuracy or completeness of the content and assumes no liability for any damages resulting from its use.

Semperis, original article

Original text: “Identity Crisis: Novel Vulnerabilities Leading to Kerberos Downgrade, DoS, and Full Domain Takeover” by Shai Laron, Security Researcher, at Semperis.

oxfemale Vulnerability research, reverse engineering, and exploit development.
// Discussion