From 3dfc0a2cccd1e37d04c0327c2d5ed323397e677c Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Tue, 29 Sep 2026 09:56:41 -0500 Subject: [PATCH] Fix PingID credential-reset username matching and reset selection Normalize UPN and domain-prefixed usernames on both sides of the Windows password-event join while preserving the original PingID user in findings. Consider all matching password events and retain the nearest qualifying reset per complete stats group, preserving source-distinct pairings. Add the synthetic regression fixture from splunk/attack_data#1230 and increment the detection version. Local unit tests return two findings for the original fixture and four for the new fixture. Separate local assertions verify the exact results and nearest qualifying reset; the current CI unit runner only requires nonzero results. Co-Authored-By: Claude Opus 5.5 --- ...ngid_new_mfa_method_after_credential_reset.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/detections/application/pingid_new_mfa_method_after_credential_reset.yml b/detections/application/pingid_new_mfa_method_after_credential_reset.yml index 534ffc8cb2..a9226ce3e8 100644 --- a/detections/application/pingid_new_mfa_method_after_credential_reset.yml +++ b/detections/application/pingid_new_mfa_method_after_credential_reset.yml @@ -1,15 +1,15 @@ name: PingID New MFA Method After Credential Reset id: 2fcbce12-cffa-4c84-b70c-192604d201d0 -version: 9 +version: 10 creation_date: '2023-12-20' -modification_date: '2026-05-13' +modification_date: '2026-09-29' author: Steven Dick status: production type: TTP description: The following analytic identifies the provisioning of a new MFA device shortly after a password reset. It detects this activity by correlating Windows Event Log events for password changes (EventID 4723, 4724) with PingID logs indicating device pairing. This behavior is significant as it may indicate a social engineering attack where a threat actor impersonates a valid user to reset credentials and add a new MFA device. If confirmed malicious, this activity could allow an attacker to gain persistent access to the compromised account, bypassing traditional security measures. data_source: - PingID -search: "`pingid` \"result.message\" = \"*Device Paired*\" | rex field=result.message \"Device (Unp)?(P)?aired (?.+)\" | eval src = coalesce('resources{}.ipaddress','resources{}.devicemodel'), user = upper('actors{}.name'), reason = 'result.message' | eval object=CASE(ISNOTNULL('resources{}.devicemodel'),'resources{}.devicemodel',true(),device_extract) | eval action=CASE(match('result.message',\"Device Paired*\"),\"created\",match('result.message', \"Device Unpaired*\"),\"deleted\") | stats count min(_time) as firstTime, max(_time) as lastTime, values(reason) as reason by src,user,action,object | join type=outer user [| search `wineventlog_security` EventID IN(4723,4724) | eval PW_Change_Time = _time, user = upper(user) | fields user,src_user,EventID,PW_Change_Time] | eval timeDiffRaw = round(lastTime - PW_Change_Time) | eval timeDiff = replace(tostring(abs(timeDiffRaw) ,\"duration\"),\"(\\d*)\\+*(\\d+):(\\d+):(\\d+)\",\"\\2 hours \\3 minutes\") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `security_content_ctime(PW_Change_Time)` | where timeDiffRaw > 0 AND timeDiffRaw < 3600 | `pingid_new_mfa_method_after_credential_reset_filter`" +search: "`pingid` \"result.message\" = \"*Device Paired*\" | rex field=result.message \"Device (Unp)?(P)?aired (?.+)\" | eval src = coalesce('resources{}.ipaddress','resources{}.devicemodel'), user = upper('actors{}.name'), reason = 'result.message' | eval object=CASE(ISNOTNULL('resources{}.devicemodel'),'resources{}.devicemodel',true(),device_extract) | eval action=CASE(match('result.message',\"Device Paired*\"),\"created\",match('result.message', \"Device Unpaired*\"),\"deleted\") | stats count min(_time) as firstTime, max(_time) as lastTime, values(reason) as reason by src,user,action,object | eval join_user = replace(user, \"^[^\\x5c]*\\x5c|@.*$\", \"\") | join type=outer max=0 join_user [| search `wineventlog_security` EventID IN(4723,4724) | eval PW_Change_Time = _time, join_user = replace(upper(user), \"^[^\\x5c]*\\x5c|@.*$\", \"\") | fields join_user,src_user,EventID,PW_Change_Time] | eval timeDiffRaw = round(lastTime - PW_Change_Time) | eval timeDiff = replace(tostring(abs(timeDiffRaw) ,\"duration\"),\"(\\d*)\\+*(\\d+):(\\d+):(\\d+)\",\"\\2 hours \\3 minutes\") | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `security_content_ctime(PW_Change_Time)` | where timeDiffRaw > 0 AND timeDiffRaw < 3600 | dedup src, user, action, object, lastTime sortby +timeDiffRaw | fields - join_user | `pingid_new_mfa_method_after_credential_reset_filter`" how_to_implement: Target environment must ingest Windows Event Log and PingID(PingOne) data sources. Specifically from logs from Active Directory Domain Controllers and JSON logging from a PingID(PingOne) enterprise environment, either via Webhook or Push Subscription. known_false_positives: False positives may be generated by normal provisioning workflows that generate a password reset followed by a device registration. references: @@ -57,3 +57,12 @@ tests: source: PINGID sourcetype: _json test_type: unit + - name: True Positive Test - UPN usernames and multiple resets + attack_data: + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/windows_pw_reset_upn_multi_reset.log + source: XmlWinEventLog:Security + sourcetype: XmlWinEventLog + - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1621/pingid/pingid_upn_multi_reset.log + source: PINGID + sourcetype: _json + test_type: unit