Skip to content

PingID credential-reset correlation: normalize usernames and handle multiple resets - #4298

Merged
nasbench merged 9 commits into
splunk:developfrom
sbaker-gre:fix/pingid-credential-reset-user-join
Oct 13, 2026
Merged

nasbench merged 9 commits into
splunk:developfrom
sbaker-gre:fix/pingid-credential-reset-user-join

Conversation

@sbaker-gre

Copy link
Copy Markdown
Contributor

PingID New MFA Method After Credential Reset can't fire in environments where PingID
usernames are UPNs or email addresses.

The search joins PingID device-pairing events to Windows 4723/4724 on user. PingID
reports the IdP username (for example victim_user@example.com), while the Windows
password events carry the bare account name (victim_user). upper() alone never makes those match.
The shipped fixture uses victim_user on both sides, so the unit test passes anyway.

Changes

  • Join on a normalized join_user, with any DOMAIN\ prefix and @domain suffix stripped
    on both sides. The finding keeps the original PingID user.
  • join max=0, so every password event is considered, not only the first one the subsearch
    returns. Previously, a reset logged after the pairing could hide an earlier one that
    qualifies.
  • dedup src, user, action, object, lastTime sortby +timeDiffRaw: one finding per
    stats group, using the nearest qualifying reset. Pairings from different source IPs
    stay separate.
  • The subsearch no longer returns user, so it can't overwrite the PingID value.
  • A second unit test uses the new fixture from T1621 PingID: add UPN and multiple-reset regression fixture attack_data#1230: UPN users, a
    reset after the pairing ahead of one that qualifies, a reset outside the window, and
    simultaneous pairings from two IPs.
  • Version 9 → 10.

Verification

  • Unit tests with the new fixture: fixed search 4 results (expected 1 + 1 + 0 + 2); the
    current search returns 0. Both pass the original fixture (2 results). Run with
    contentctl-ng 1.1.1, Splunk 10.4.3, Splunk_TA_windows 11.0.2 and Splunk_TA_fix_windows.
  • Separate local assertions verified the exact user/source results and nearest qualifying
    reset. The current CI unit runner checks for nonzero results; it does not enforce the
    exact count or selected reset.
  • The new unit test depends on the attack_data PR merging first.

🤖 Generated with Claude Code

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 <noreply@anthropic.com>
sbaker-gre added a commit to sbaker-gre/attack_data that referenced this pull request Sep 29, 2026
Add five synthetic PingID device-pairing events and six Windows 4723/4724
password events for PingID New MFA Method After Credential Reset.
Cover UPN-to-bare username matching, multiple resets and nearest-reset
selection, exclusion outside the one-hour window, and simultaneous
pairings from distinct source IPs. Register both files in the manifest.

Use lab identities and reserved documentation IPs. Companion to
splunk/security_content#4298.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sbaker-gre
sbaker-gre force-pushed the fix/pingid-credential-reset-user-join branch from f517cc5 to 3dfc0a2 Compare September 29, 2026 19:09
@nasbench nasbench added this to the v6.9.0 milestone Oct 1, 2026
onurmerdogan and others added 2 commits October 9, 2026 16:17
In an eval string literal "\\" reaches the regex engine as a single
backslash, so "[^\\]*\\|@.*$" parses as an unterminated character
class and the search fails: "Regex: missing terminating ] for
character class". \x5c survives both YAML and SPL unescaping. Also
restores the ^ anchor on the PingID side to match the Windows side.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sbaker-gre

Copy link
Copy Markdown
Contributor Author

Thanks for the reformat. One follow-up commit (1f9689d): in an eval string literal "\\" reaches the regex engine as a single backslash, so "[^\\]*\\|@.*$" became an unterminated character class and the search failed at parse time (Regex: missing terminating ] for character class). Restored the \x5c form on both replace() lines, which survives both YAML and SPL unescaping, and put the ^ anchor back on the PingID side so it matches the Windows side.

Repro:

| makeresults | eval u="CORP\\alice" | eval u2=replace(upper(u),"[^\\]*\\|@.*$","")

@onurmerdogan

Copy link
Copy Markdown
Contributor

Thanks for the reformat. One follow-up commit (1f9689d): in an eval string literal "\\" reaches the regex engine as a single backslash, so "[^\\]*\\|@.*$" became an unterminated character class and the search failed at parse time (Regex: missing terminating ] for character class). Restored the \x5c form on both replace() lines, which survives both YAML and SPL unescaping, and put the ^ anchor back on the PingID side so it matches the Windows side.

Repro:

| makeresults | eval u="CORP\\alice" | eval u2=replace(upper(u),"[^\\]*\\|@.*$","")

Thanks a lot for the fix, my main goal was making the regex easier to review but I see some special chars can cause an issue at times(\x27 for single-quote etc.)

I just added a comment there for future reviewers/users who might not be familiar with corresponding data sources.

@nasbench

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T17:10:29.382988Z c9e519f Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@nasbench

Copy link
Copy Markdown
Contributor
image

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77812d5c6e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread detections/application/pingid_new_mfa_method_after_credential_reset.yml Outdated
The reformat normalized user in place, so findings and drilldowns showed
the bare account name instead of the PingID username used by the other
PingID detections. Join on join_user again and drop it before output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nasbench

Copy link
Copy Markdown
Contributor

@sbaker-gre thanks for the update! Looks like you (or claude) nuked the comments added by @onurmerdogan - Let's add those back for clarity. While codex does a final review.

@nasbench

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: c9e519f406

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sbaker-gre

Copy link
Copy Markdown
Contributor Author

@sbaker-gre thanks for the update! Looks like you (or claude) nuked the comments added by @onurmerdogan - Let's add those back for clarity. While codex does a final review.

Yep, my bad. I trusted claude with that one and darn it...

@nasbench nasbench left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Tested locally and results are showing up (see my screenshot above).

PS: due to how ingest time work via HEC, I had to cheat a little by using the event time rather than the detection _time (but in practice this should not be an issue when ingesting via forwarders or uploading).

As an extra note. Since CI is failing for external contributors, I might have missed something. We will run full testing as always internally and fix any issues that comes up.

@nasbench
nasbench merged commit a3936a0 into splunk:develop Oct 13, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants