Techniques
Sample rules
Windows AD SPN Unicode Collision Injection
- source: splunk
- technicques:
Description
The following analytic detects the addition or modification of a servicePrincipalName (SPN) containing invisible Unicode characters to an Active Directory computer object via Windows Security Event 5136. This is the key indicator of the KerberLoss attack (CVE-2026-25177), where an attacker with write access to a machine account’s SPN attribute injects a collision SPN that contains a Zero Width or other invisible Unicode character (e.g. U+200C Zero Width Non-Joiner). LDAP’s string preparation rules (RFC 4518) cause these characters to be ignored during uniqueness checking, allowing the write to succeed even when an identical SPN already exists on another machine account. The Kerberos KDC, however, does not apply the same normalisation — it finds two accounts with matching SPNs and returns KDC_ERR_S_PRINCIPAL_UNKNOWN, causing a Kerberos denial-of-service or forcing clients to fall back to NTLM downgrade.
Detection logic
`wineventlog_security`
EventCode=5136
AttributeValue=*
AttributeLDAPDisplayName=servicePrincipalName
OperationType IN (
"%%14674",
"%%14675"
)
| regex AttributeValue="[\x{034F}\x{200B}-\x{200F}\x{00AD}\x{1806}\x{FEFF}\x{FFFC}\x{2060}\x{206A}-\x{206F}\x{180B}-\x{180D}\x{FE00}-\x{FE0F}]"
| eval matched_invisible_chars=mvappend(
if(match(AttributeValue, "\x{034F}"), "U+034F (Combining Grapheme Joiner)", null()),
if(match(AttributeValue, "\x{200B}"), "U+200B (Zero Width Space)", null()),
if(match(AttributeValue, "\x{200C}"), "U+200C (Zero Width Non-Joiner)", null()),
if(match(AttributeValue, "\x{200D}"), "U+200D (Zero Width Joiner)", null()),
if(match(AttributeValue, "\x{200E}"), "U+200E (Left-to-Right Mark)", null()),
if(match(AttributeValue, "\x{200F}"), "U+200F (Right-to-Left Mark)", null()),
if(match(AttributeValue, "\x{00AD}"), "U+00AD (Soft Hyphen)", null()),
if(match(AttributeValue, "\x{1806}"), "U+1806 (Mongolian Todo Soft Hyphen)", null()),
if(match(AttributeValue, "\x{FEFF}"), "U+FEFF (Zero Width No-Break Space)", null()),
if(match(AttributeValue, "\x{FFFC}"), "U+FFFC (Object Replacement Character)", null()),
if(match(AttributeValue, "\x{2060}"), "U+2060 (Word Joiner)", null()),
if(match(AttributeValue, "[\x{206A}-\x{206F}]"), "U+206A-206F (Deprecated Format Characters)", null()),
if(match(AttributeValue, "[\x{180B}-\x{180D}]"), "U+180B-180D (Variation Selectors)", null()),
if(match(AttributeValue, "[\x{FE00}-\x{FE0F}]"), "U+FE00-FE0F (Variation Selectors)", null()))
| where isnotnull(matched_invisible_chars)
| rename Computer as dest
| stats count min(_time) as firstTime
max(_time) as lastTime
values(matched_invisible_chars) as matched_invisible_chars
by dest SubjectUserName SubjectDomainName ObjectDN ObjectClass AttributeValue
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `windows_ad_spn_unicode_collision_injection_filter`