Potential Shadow Credentials added to AD Object

Identify the modification of the msDS-KeyCredentialLink attribute in an Active Directory Computer or User Object. Attackers can abuse control over the object and create a key pair, append to raw public key in the attribute, and obtain persistent and stealthy access to the target user or computer object.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2022/01/26"
  3integration = ["system", "windows"]
  4maturity = "production"
  5updated_date = "2026/04/27"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identify the modification of the msDS-KeyCredentialLink attribute in an Active Directory Computer or User Object.
 11Attackers can abuse control over the object and create a key pair, append to raw public key in the attribute, and obtain
 12persistent and stealthy access to the target user or computer object.
 13"""
 14false_positives = [
 15    """
 16    Azure AD Connect or ADFS provisioning can legitimately modify msDS-KeyCredentialLink when the writer account,
 17    source, object class, target DN, bounded change set, and post-change authentication all match an expected workflow.
 18    """,
 19]
 20from = "now-9m"
 21index = ["winlogbeat-*", "logs-system.security*", "logs-windows.forwarded*"]
 22language = "kuery"
 23license = "Elastic License v2"
 24name = "Potential Shadow Credentials added to AD Object"
 25references = [
 26    "https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab",
 27    "https://www.thehacker.recipes/ad/movement/kerberos/shadow-credentials",
 28    "https://github.com/OTRF/Set-AuditRule",
 29    "https://cyberstoph.org/posts/2022/03/detecting-shadow-credentials/",
 30]
 31risk_score = 73
 32rule_id = "79f97b31-480e-4e63-a7f4-ede42bf2c6de"
 33severity = "high"
 34tags = [
 35    "Domain: Endpoint",
 36    "OS: Windows",
 37    "Use Case: Threat Detection",
 38    "Tactic: Credential Access",
 39    "Data Source: Active Directory",
 40    "Resources: Investigation Guide",
 41    "Use Case: Active Directory Monitoring",
 42    "Data Source: Windows Security Event Logs",
 43]
 44timestamp_override = "event.ingested"
 45type = "query"
 46
 47query = '''
 48event.code:"5136" and host.os.type:"windows" and winlog.event_data.AttributeLDAPDisplayName:"msDS-KeyCredentialLink" and
 49  winlog.event_data.AttributeValue :B\:828* and
 50  not winlog.event_data.SubjectUserName: MSOL_* and
 51  not winlog.event_data.ObjectClass: "msDS-Device"
 52'''
 53
 54note = """## Triage and analysis
 55
 56### Investigating Potential Shadow Credentials added to AD Object
 57
 58#### Possible investigation steps
 59
 60- What object received the key-trust change?
 61  - Focus: `winlog.event_data.ObjectDN`, `winlog.event_data.ObjectGUID`, `winlog.event_data.ObjectClass`, `winlog.event_data.OperationType`, and `winlog.event_data.AttributeValue`.
 62  - Implication: escalate when a sensitive user or computer receives an added or replaced key-trust value outside recognized enrollment; lower suspicion only when the object class, DN, and operation fit the same identity-registration workflow.
 63
 64- Which account and logon session wrote the value?
 65  - Focus: `winlog.event_data.SubjectUserSid`, `winlog.event_data.SubjectUserName`, `winlog.event_data.SubjectLogonId`, recovered `source.ip`, and `winlog.logon.type`. $investigate_0
 66  - Hint: match alert `winlog.event_data.SubjectLogonId` to same `host.id` authentication events with `winlog.event_data.TargetLogonId`; if no 4624 matches, keep the session unresolved.
 67  - Implication: escalate for an unexpected user, admin, service, or machine writer, or source/logon type outside the object's enrollment path; lower suspicion when writer and session match recognized provisioning or device registration.
 68
 69- Does the writer/object pair fit a recognized ADFS or Azure AD Connect-style path?
 70  - Why: the abuse path writes authentication material, so service-looking writers still need source and change-set validation.
 71  - Focus: compare `winlog.event_data.SubjectUserSid`, `winlog.event_data.SubjectUserName`, `winlog.event_data.ObjectClass`, `winlog.event_data.ObjectDN`, and `source.ip` to the expected service account, object type, and enrollment source.
 72  - Implication: lower suspicion when recognized provisioning updates the expected object from the expected source; escalate when the writer is ad hoc, interactive, non-provisioning, object-class mismatched, or unexplained by source.
 73
 74- Was the logical change limited to this key credential?
 75  - Focus: use same-operation 5136 events grouped by `winlog.event_data.OpCorrelationID`; compare `winlog.event_data.ObjectGUID`, `winlog.event_data.AttributeLDAPDisplayName`, `winlog.event_data.OperationType`, and `winlog.event_data.AttributeValue`. $investigate_1
 76  - Implication: escalate when the operation touches unrelated objects, adds other authentication or delegation material, or removes cleanup evidence; lower suspicion when bounded to the expected object and enrollment attributes.
 77
 78- Did the modified identity authenticate after the change?
 79  - Why: post-change authentication shows whether the new key material may already be in use.
 80  - Focus: derive the principal from `winlog.event_data.ObjectDN`; review authentication events for `winlog.event_data.TargetUserName`, `winlog.event_data.TargetDomainName`, `source.ip`, `winlog.logon.type`, and `winlog.event_data.AuthenticationPackageName`.
 81  - Hint: search after `@timestamp` for Target-side fields matching the derived principal; if `source.ip` is empty, lower origin confidence instead of treating absence as benign.
 82  - Implication: escalate when the identity authenticates from a new source, unexpected logon type, or authentication path after the change; absence of follow-on use reduces urgency only when earlier evidence proves recognized provisioning.
 83
 84- Do related alerts change the scope beyond this object?
 85  - Focus: recent alerts for the modifying account using `user.id` or `winlog.event_data.SubjectUserSid`. $investigate_2
 86  - Hint: compare with alerts scoped to the modified object's `winlog.event_data.ObjectGUID`. $investigate_3
 87  - Implication: broaden response when either scope shows privilege abuse, directory tampering, relay activity, or lateral movement; keep local when related alerts are quiet and local evidence resolves to one recognized workflow.
 88
 89- Escalate on the key-trust change plus any suspicious or unresolved object, writer session, `winlog.event_data.OpCorrelationID` scope, post-change authentication, or related-alert finding; close only when all evidence binds to one recognized provisioning workflow; preserve and escalate when evidence is mixed, incomplete, or uncorroborated.
 90
 91### False positive analysis
 92
 93- AutoPilot or WHfB device enrollment can cause a computer to write its own key credential. Confirm `winlog.event_data.SubjectUserName` matches the CN in `winlog.event_data.ObjectDN`, `winlog.event_data.ObjectClass` is "computer", `winlog.event_data.OpCorrelationID` is bounded, and no unexpected follow-on authentication occurs.
 94- ADFS or Azure AD Connect provisioning can update key credentials on user or computer objects. Confirm `winlog.event_data.SubjectUserSid`, `winlog.event_data.ObjectDN`, recovered `source.ip`, bounded `winlog.event_data.OpCorrelationID`, and post-change authentication align with one named workflow. Keep open when ownership is unresolved.
 95- Build exceptions from stable writer SID, object class or GUID, `host.id`, recovered source, and enrollment path across prior alerts. Avoid exceptions on "msDS-KeyCredentialLink", `user.name`, or host alone.
 96
 97### Response and remediation
 98
 99- Preserve a case export of the triggering 5136, recovered writer-session authentication events, `winlog.event_data.AttributeValue`, `winlog.event_data.ObjectGUID`, and `winlog.event_data.OpCorrelationID` before containment, reversal, or cleanup.
100- If confirmed benign, reverse temporary containment and document the exact workflow evidence: writer SID, object GUID/class, domain naming context, recovered source, bounded change set, and post-change authentication pattern. Keep any exception narrow and only for the recurring workflow.
101- If suspicious but unconfirmed, apply reversible controls to the writer first, such as heightened monitoring or temporary access review; restrict the modified identity only when object sensitivity or follow-on authentication shows active risk.
102- If confirmed malicious, contain the writer account or source system using `winlog.event_data.SubjectLogonId`, `source.ip`, `host.id`, and follow-on authentication evidence. Disable the writer first when its session performed unauthorized changes; disable or rotate the modified identity only when post-change authentication or object sensitivity shows active risk.
103- After containment, remove only the unauthorized key-trust value and verify rollback. Reset or rotate the modified identity according to `winlog.event_data.ObjectClass`: reset user passwords, rotate service credentials, or re-establish the expected computer trust path. Review the same `winlog.event_data.OpCorrelationID` or session for additional unauthorized changes.
104- Post-incident hardening: restrict write access to "msDS-KeyCredentialLink" to dedicated identity-management accounts, retain 5136 auditing on domain controllers, and record the confirmed provisioning workflow or abuse pattern for future triage.
105"""
106
107setup = """## Setup
108
109Audit Directory Service Changes must be enabled to generate the events used by this rule.
110Setup instructions: https://ela.st/audit-directory-service-changes
111"""
112
113[rule.investigation_fields]
114field_names = [
115    "@timestamp",
116    "user.id",
117    "winlog.event_data.SubjectUserSid",
118    "winlog.event_data.SubjectUserName",
119    "winlog.event_data.SubjectDomainName",
120    "winlog.event_data.SubjectLogonId",
121    "winlog.event_data.ObjectDN",
122    "winlog.event_data.ObjectGUID",
123    "winlog.event_data.ObjectClass",
124    "winlog.event_data.AttributeLDAPDisplayName",
125    "winlog.event_data.AttributeValue",
126    "winlog.event_data.OperationType",
127    "winlog.event_data.OpCorrelationID",
128    "host.name",
129    "host.id",
130]
131
132[transform]
133
134[[transform.investigate]]
135label = "Successful logon for the modifying session"
136description = ""
137providers = [
138  [
139    { excluded = false, field = "event.code", queryType = "phrase", value = "4624", valueType = "string" },
140    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
141    { excluded = false, field = "winlog.event_data.TargetLogonId", queryType = "phrase", value = "{{winlog.event_data.SubjectLogonId}}", valueType = "string" }
142  ]
143]
144relativeFrom = "now-48h/h"
145relativeTo = "now"
146
147[[transform.investigate]]
148label = "Directory changes in the same operation"
149description = ""
150providers = [
151  [
152    { excluded = false, field = "event.code", queryType = "phrase", value = "5136", valueType = "string" },
153    { excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
154    { excluded = false, field = "winlog.event_data.OpCorrelationID", queryType = "phrase", value = "{{winlog.event_data.OpCorrelationID}}", valueType = "string" }
155  ]
156]
157relativeFrom = "now-48h/h"
158relativeTo = "now"
159
160[[transform.investigate]]
161label = "Alerts associated with the modifying account"
162description = ""
163providers = [
164  [
165    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
166    { excluded = false, field = "user.id", queryType = "phrase", value = "{{user.id}}", valueType = "string" }
167  ]
168]
169relativeFrom = "now-48h/h"
170relativeTo = "now"
171
172[[transform.investigate]]
173label = "Alerts associated with the modified object"
174description = ""
175providers = [
176  [
177    { excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
178    { excluded = false, field = "winlog.event_data.ObjectGUID", queryType = "phrase", value = "{{winlog.event_data.ObjectGUID}}", valueType = "string" }
179  ]
180]
181relativeFrom = "now-48h/h"
182relativeTo = "now"
183
184[[rule.threat]]
185framework = "MITRE ATT&CK"
186
187[[rule.threat.technique]]
188id = "T1556"
189name = "Modify Authentication Process"
190reference = "https://attack.mitre.org/techniques/T1556/"
191
192[rule.threat.tactic]
193id = "TA0006"
194name = "Credential Access"
195reference = "https://attack.mitre.org/tactics/TA0006/"
196
197[[rule.threat]]
198framework = "MITRE ATT&CK"
199
200[[rule.threat.technique]]
201id = "T1098"
202name = "Account Manipulation"
203reference = "https://attack.mitre.org/techniques/T1098/"
204
205[rule.threat.tactic]
206id = "TA0003"
207name = "Persistence"
208reference = "https://attack.mitre.org/tactics/TA0003/"

Triage and analysis

Investigating Potential Shadow Credentials added to AD Object

Possible investigation steps

  • What object received the key-trust change?

    • Focus: winlog.event_data.ObjectDN, winlog.event_data.ObjectGUID, winlog.event_data.ObjectClass, winlog.event_data.OperationType, and winlog.event_data.AttributeValue.
    • Implication: escalate when a sensitive user or computer receives an added or replaced key-trust value outside recognized enrollment; lower suspicion only when the object class, DN, and operation fit the same identity-registration workflow.
  • Which account and logon session wrote the value?

    • Focus: winlog.event_data.SubjectUserSid, winlog.event_data.SubjectUserName, winlog.event_data.SubjectLogonId, recovered source.ip, and winlog.logon.type. $investigate_0
    • Hint: match alert winlog.event_data.SubjectLogonId to same host.id authentication events with winlog.event_data.TargetLogonId; if no 4624 matches, keep the session unresolved.
    • Implication: escalate for an unexpected user, admin, service, or machine writer, or source/logon type outside the object's enrollment path; lower suspicion when writer and session match recognized provisioning or device registration.
  • Does the writer/object pair fit a recognized ADFS or Azure AD Connect-style path?

    • Why: the abuse path writes authentication material, so service-looking writers still need source and change-set validation.
    • Focus: compare winlog.event_data.SubjectUserSid, winlog.event_data.SubjectUserName, winlog.event_data.ObjectClass, winlog.event_data.ObjectDN, and source.ip to the expected service account, object type, and enrollment source.
    • Implication: lower suspicion when recognized provisioning updates the expected object from the expected source; escalate when the writer is ad hoc, interactive, non-provisioning, object-class mismatched, or unexplained by source.
  • Was the logical change limited to this key credential?

    • Focus: use same-operation 5136 events grouped by winlog.event_data.OpCorrelationID; compare winlog.event_data.ObjectGUID, winlog.event_data.AttributeLDAPDisplayName, winlog.event_data.OperationType, and winlog.event_data.AttributeValue. $investigate_1
    • Implication: escalate when the operation touches unrelated objects, adds other authentication or delegation material, or removes cleanup evidence; lower suspicion when bounded to the expected object and enrollment attributes.
  • Did the modified identity authenticate after the change?

    • Why: post-change authentication shows whether the new key material may already be in use.
    • Focus: derive the principal from winlog.event_data.ObjectDN; review authentication events for winlog.event_data.TargetUserName, winlog.event_data.TargetDomainName, source.ip, winlog.logon.type, and winlog.event_data.AuthenticationPackageName.
    • Hint: search after @timestamp for Target-side fields matching the derived principal; if source.ip is empty, lower origin confidence instead of treating absence as benign.
    • Implication: escalate when the identity authenticates from a new source, unexpected logon type, or authentication path after the change; absence of follow-on use reduces urgency only when earlier evidence proves recognized provisioning.
  • Do related alerts change the scope beyond this object?

    • Focus: recent alerts for the modifying account using user.id or winlog.event_data.SubjectUserSid. $investigate_2
    • Hint: compare with alerts scoped to the modified object's winlog.event_data.ObjectGUID. $investigate_3
    • Implication: broaden response when either scope shows privilege abuse, directory tampering, relay activity, or lateral movement; keep local when related alerts are quiet and local evidence resolves to one recognized workflow.
  • Escalate on the key-trust change plus any suspicious or unresolved object, writer session, winlog.event_data.OpCorrelationID scope, post-change authentication, or related-alert finding; close only when all evidence binds to one recognized provisioning workflow; preserve and escalate when evidence is mixed, incomplete, or uncorroborated.

False positive analysis

  • AutoPilot or WHfB device enrollment can cause a computer to write its own key credential. Confirm winlog.event_data.SubjectUserName matches the CN in winlog.event_data.ObjectDN, winlog.event_data.ObjectClass is "computer", winlog.event_data.OpCorrelationID is bounded, and no unexpected follow-on authentication occurs.
  • ADFS or Azure AD Connect provisioning can update key credentials on user or computer objects. Confirm winlog.event_data.SubjectUserSid, winlog.event_data.ObjectDN, recovered source.ip, bounded winlog.event_data.OpCorrelationID, and post-change authentication align with one named workflow. Keep open when ownership is unresolved.
  • Build exceptions from stable writer SID, object class or GUID, host.id, recovered source, and enrollment path across prior alerts. Avoid exceptions on "msDS-KeyCredentialLink", user.name, or host alone.

Response and remediation

  • Preserve a case export of the triggering 5136, recovered writer-session authentication events, winlog.event_data.AttributeValue, winlog.event_data.ObjectGUID, and winlog.event_data.OpCorrelationID before containment, reversal, or cleanup.
  • If confirmed benign, reverse temporary containment and document the exact workflow evidence: writer SID, object GUID/class, domain naming context, recovered source, bounded change set, and post-change authentication pattern. Keep any exception narrow and only for the recurring workflow.
  • If suspicious but unconfirmed, apply reversible controls to the writer first, such as heightened monitoring or temporary access review; restrict the modified identity only when object sensitivity or follow-on authentication shows active risk.
  • If confirmed malicious, contain the writer account or source system using winlog.event_data.SubjectLogonId, source.ip, host.id, and follow-on authentication evidence. Disable the writer first when its session performed unauthorized changes; disable or rotate the modified identity only when post-change authentication or object sensitivity shows active risk.
  • After containment, remove only the unauthorized key-trust value and verify rollback. Reset or rotate the modified identity according to winlog.event_data.ObjectClass: reset user passwords, rotate service credentials, or re-establish the expected computer trust path. Review the same winlog.event_data.OpCorrelationID or session for additional unauthorized changes.
  • Post-incident hardening: restrict write access to "msDS-KeyCredentialLink" to dedicated identity-management accounts, retain 5136 auditing on domain controllers, and record the confirmed provisioning workflow or abuse pattern for future triage.

References

Related rules

to-top