From 3f1c510e486d72f7e48ec09fce85dc3936b40903 Mon Sep 17 00:00:00 2001 From: Raven Tait Date: Thu, 24 Sep 2026 10:20:10 -0400 Subject: [PATCH 1/3] ResetNightmare --- .../windows_ad_user_suspicious_upn_change.yml | 65 +++++++++++++++++++ ...ws_admin_password_changed_by_non_admin.yml | 16 ++--- 2 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 detections/endpoint/windows_ad_user_suspicious_upn_change.yml diff --git a/detections/endpoint/windows_ad_user_suspicious_upn_change.yml b/detections/endpoint/windows_ad_user_suspicious_upn_change.yml new file mode 100644 index 0000000000..f109100eb0 --- /dev/null +++ b/detections/endpoint/windows_ad_user_suspicious_upn_change.yml @@ -0,0 +1,65 @@ +name: Windows AD User Suspicious UPN Change +id: 81324b8b-46c0-4ca3-b9a1-d33638f21021 +version: 1 +creation_date: '2026-09-23' +modification_date: '2026-09-23' +author: Raven Tait, Splunk +status: production +type: TTP +description: The following analytic detects a user account modifying its own userPrincipalName (UPN) to match the sAMAccountName of another account via Windows Security Event 4738. This is the setup step of the ResetNightmare attack (CVE-2026-27912), where an attacker with WriteProperty rights on their own UPN attribute spoofs their identity to a target account. The KDC then resolves a ptype=10 (NT-ENTERPRISE) Kerberos pre-authentication request against the spoofed UPN, issuing a kadmin/changepw TGT that can be used to change the target account's password via kpasswd (port 464). Event 4738 is generated when a user account attribute is changed. This analytic filters to events where the SubjectUserSid equals the TargetSid (self-modification), the new UPN value is not a standard UPN (no @ sign), and the value is not a Windows placeholder. A non-UPN value set on one's own account is anomalous and has no legitimate administrative use case. +data_source: + - Windows Event Log Security 4738 +search: |- + `wineventlog_security` EventCode=4738 + | where SubjectUserSid=TargetSid + | where UserPrincipalName!="%%1793" AND UserPrincipalName!="-" + AND isnotnull(UserPrincipalName) AND UserPrincipalName!="" + | where NOT match(UserPrincipalName, "@") + | where NOT SubjectUserSid IN ("S-1-5-18", "S-1-5-7") + AND NOT match(SubjectUserName, "^(NT AUTHORITY|ANONYMOUS)") + | stats count min(_time) as firstTime max(_time) as lastTime + by dest SubjectUserName SubjectUserSid TargetUserName TargetSid UserPrincipalName + | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` + | `windows_ad_user_suspicious_upn_change_filter` +how_to_implement: To successfully implement this search, you need to be ingesting Domain Controller events. The Advanced Security Audit policy setting `User Account Management` within `Account Management` needs to be enabled. +known_false_positives: Administrators or identity management tools may legitimately set a UPN that does not contain an @ sign during account provisioning or migration workflows. Investigate the SubjectUserName and context to confirm. +references: + - https://cravaterouge.com/articles/resetnightmare/ + - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-27912 + - https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4738 +drilldown_searches: + - name: View the detection results for - "$SubjectUserName$" + search: '%original_detection_search% | search SubjectUserName = "$SubjectUserName$"' + earliest_offset: $info_min_time$ + latest_offset: $info_max_time$ + - name: View risk events for the last 7 days for - "$SubjectUserName$" + search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$SubjectUserName$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' + earliest_offset: 7d + latest_offset: "0" +finding: + title: User $SubjectUserName$ set their UPN to $UserPrincipalName$ on $dest$ + entity: + field: SubjectUserName + type: user + score: 50 +analytic_story: + - Active Directory Kerberos Attacks +asset_type: Endpoint +cve: + - CVE-2026-27912 +mitre_attack_id: + - T1078.002 +product: + - Splunk Enterprise + - Splunk Enterprise Security + - Splunk Cloud +category: endpoint +security_domain: endpoint +tests: + - name: True Positive Test + attack_data: + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1078.002/resetnightmare/windows-xml.log + source: XmlWinEventLog:Security + sourcetype: XmlWinEventLog + test_type: unit diff --git a/detections/endpoint/windows_admin_password_changed_by_non_admin.yml b/detections/endpoint/windows_admin_password_changed_by_non_admin.yml index 7bb99a45b9..ca71fc4b6a 100644 --- a/detections/endpoint/windows_admin_password_changed_by_non_admin.yml +++ b/detections/endpoint/windows_admin_password_changed_by_non_admin.yml @@ -1,8 +1,8 @@ name: Windows Admin Password Changed by Non-Admin id: 6d4c4d88-cd60-43a1-8c70-c74a9614f724 -version: 1 +version: 2 creation_date: '2026-04-27' -modification_date: '2026-04-27' +modification_date: '2026-09-23' author: Raven Tait, Splunk status: production type: TTP @@ -13,15 +13,12 @@ data_source: search: |- `wineventlog_security` EventCode=4723 - | rex field=object_id "-(?\d+)$" + | rex field=TargetSid "-(?\d+)$" | rex field=SubjectUserSid "-(?\d+)$" - | where target_rid="500" OR tonumber(target_rid) IN (512,513,518,519,520) + | where tonumber(target_rid)=500 OR tonumber(target_rid) IN (512,513,518,519,520) | where tonumber(subject_rid) >= 1000 - | where SubjectUserSid != object_id - | stats count min(_time) as firstTime - max(_time) as lastTime - by dest user object_id EventCode src_user - SubjectUserSid SubjectLogonId PrivilegeList + | where subject_rid != target_rid + | stats count min(_time) as firstTime max(_time) as lastTime by dest TargetSid EventCode SubjectUserSid src_user | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `windows_admin_password_changed_by_non_admin_filter` @@ -52,6 +49,7 @@ analytic_story: asset_type: Endpoint cve: - CVE-2026-33825 + - CVE-2026-27912 mitre_attack_id: - T1068 - T1543.003 From 56707416edbfbbac6252babadb59608837cb25ed Mon Sep 17 00:00:00 2001 From: nasbench <8741929+nasbench@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:09:56 +0200 Subject: [PATCH 2/3] small perf update by moving checks earlier and metadata update --- .../windows_ad_user_suspicious_upn_change.yml | 50 ++++++++++++++----- ...ws_admin_password_changed_by_non_admin.yml | 22 +++++++- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/detections/endpoint/windows_ad_user_suspicious_upn_change.yml b/detections/endpoint/windows_ad_user_suspicious_upn_change.yml index f109100eb0..aa955d2dd7 100644 --- a/detections/endpoint/windows_ad_user_suspicious_upn_change.yml +++ b/detections/endpoint/windows_ad_user_suspicious_upn_change.yml @@ -6,24 +6,47 @@ modification_date: '2026-09-23' author: Raven Tait, Splunk status: production type: TTP -description: The following analytic detects a user account modifying its own userPrincipalName (UPN) to match the sAMAccountName of another account via Windows Security Event 4738. This is the setup step of the ResetNightmare attack (CVE-2026-27912), where an attacker with WriteProperty rights on their own UPN attribute spoofs their identity to a target account. The KDC then resolves a ptype=10 (NT-ENTERPRISE) Kerberos pre-authentication request against the spoofed UPN, issuing a kadmin/changepw TGT that can be used to change the target account's password via kpasswd (port 464). Event 4738 is generated when a user account attribute is changed. This analytic filters to events where the SubjectUserSid equals the TargetSid (self-modification), the new UPN value is not a standard UPN (no @ sign), and the value is not a Windows placeholder. A non-UPN value set on one's own account is anomalous and has no legitimate administrative use case. +description: |- + The following analytic detects a user account modifying its own userPrincipalName (UPN) to match the sAMAccountName of another account via Windows Security Event 4738. + This is the setup step of the ResetNightmare attack (CVE-2026-27912), where an attacker with WriteProperty rights on their own UPN attribute spoofs their identity to a target account. + The KDC then resolves a ptype=10 (NT-ENTERPRISE) Kerberos pre-authentication request against the spoofed UPN, issuing a kadmin/changepw TGT that can be used to change the target account's password via kpasswd (port 464). + Event 4738 is generated when a user account attribute is changed. This analytic filters to events where the SubjectUserSid equals the TargetSid (self-modification), the new UPN value is not a standard UPN (no @ sign), and the value is not a Windows placeholder. + A non-UPN value set on one's own account is anomalous and has no legitimate administrative use case. data_source: - Windows Event Log Security 4738 search: |- - `wineventlog_security` EventCode=4738 + `wineventlog_security` + EventCode=4738 + UserPrincipalName="*" + NOT UserPrincipalName IN ( + "-", + "", + "*@*", + "%%1793" + ) + NOT SubjectUserSid IN ( + "S-1-5-18", + "S-1-5-7" + ) + NOT SubjectUserName IN ( + "ANONYMOUS*", + "NT AUTHORITY*" + ) + | where SubjectUserSid=TargetSid - | where UserPrincipalName!="%%1793" AND UserPrincipalName!="-" - AND isnotnull(UserPrincipalName) AND UserPrincipalName!="" - | where NOT match(UserPrincipalName, "@") - | where NOT SubjectUserSid IN ("S-1-5-18", "S-1-5-7") - AND NOT match(SubjectUserName, "^(NT AUTHORITY|ANONYMOUS)") - | stats count min(_time) as firstTime max(_time) as lastTime - by dest SubjectUserName SubjectUserSid TargetUserName TargetSid UserPrincipalName + + | stats count min(_time) as firstTime + max(_time) as lastTime + + by dest SubjectUserName SubjectUserSid TargetUserName TargetSid UserPrincipalName + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `windows_ad_user_suspicious_upn_change_filter` -how_to_implement: To successfully implement this search, you need to be ingesting Domain Controller events. The Advanced Security Audit policy setting `User Account Management` within `Account Management` needs to be enabled. -known_false_positives: Administrators or identity management tools may legitimately set a UPN that does not contain an @ sign during account provisioning or migration workflows. Investigate the SubjectUserName and context to confirm. +how_to_implement: |- + To successfully implement this search, you need to be ingesting Domain Controller events. The Advanced Security Audit policy setting `User Account Management` within `Account Management` needs to be enabled. +known_false_positives: |- + Administrators or identity management tools may legitimately set a UPN that does not contain an @ sign during account provisioning or migration workflows. Investigate the SubjectUserName and context to confirm. references: - https://cravaterouge.com/articles/resetnightmare/ - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-27912 @@ -38,11 +61,14 @@ drilldown_searches: earliest_offset: 7d latest_offset: "0" finding: - title: User $SubjectUserName$ set their UPN to $UserPrincipalName$ on $dest$ + title: User [$SubjectUserName$] set their UPN to [$UserPrincipalName$] on $dest$ entity: field: SubjectUserName type: user score: 50 +threat_objects: + - field: SubjectUserName + type: user analytic_story: - Active Directory Kerberos Attacks asset_type: Endpoint diff --git a/detections/endpoint/windows_admin_password_changed_by_non_admin.yml b/detections/endpoint/windows_admin_password_changed_by_non_admin.yml index ca71fc4b6a..c65e95b2b5 100644 --- a/detections/endpoint/windows_admin_password_changed_by_non_admin.yml +++ b/detections/endpoint/windows_admin_password_changed_by_non_admin.yml @@ -13,12 +13,27 @@ data_source: search: |- `wineventlog_security` EventCode=4723 + TargetSid IN ( + "*500*", + "*512*", + "*513*", + "*518*", + "*519*", + "*520*" + ) + | rex field=TargetSid "-(?\d+)$" | rex field=SubjectUserSid "-(?\d+)$" + | where tonumber(target_rid)=500 OR tonumber(target_rid) IN (512,513,518,519,520) | where tonumber(subject_rid) >= 1000 | where subject_rid != target_rid - | stats count min(_time) as firstTime max(_time) as lastTime by dest TargetSid EventCode SubjectUserSid src_user + + | stats count min(_time) as firstTime + max(_time) as lastTime + + by dest TargetSid EventCode SubjectUserSid src_user + | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `windows_admin_password_changed_by_non_admin_filter` @@ -38,11 +53,14 @@ drilldown_searches: name: View risk events for the last 7 days for - "$dest$" search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$dest$") starthoursago=168 | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`' finding: - title: Non-Administrator account $src_user$ changed password of Admin account $user$ on $dest$. + title: Non-Administrator account [$src_user$] changed the password of the Admin account [$user$] on $dest$. entity: field: dest type: system score: 50 +threat_objects: + - field: src_user + type: user analytic_story: - Windows Privilege Escalation - BlueHammer From 29d4d4225f7204fe501edeecfb4c997150b88667 Mon Sep 17 00:00:00 2001 From: Raven Tait Date: Tue, 29 Sep 2026 12:58:06 -0400 Subject: [PATCH 3/3] Add TargetUserName to stats --- .../endpoint/windows_admin_password_changed_by_non_admin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/endpoint/windows_admin_password_changed_by_non_admin.yml b/detections/endpoint/windows_admin_password_changed_by_non_admin.yml index c65e95b2b5..3c8a2b17db 100644 --- a/detections/endpoint/windows_admin_password_changed_by_non_admin.yml +++ b/detections/endpoint/windows_admin_password_changed_by_non_admin.yml @@ -32,7 +32,7 @@ search: |- | stats count min(_time) as firstTime max(_time) as lastTime - by dest TargetSid EventCode SubjectUserSid src_user + by dest TargetSid EventCode SubjectUserSid src_user TargetUserName | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`