Skip to content

fix(crowdstrike): keep command lines intact and repair rule conditions and grouping - #2744

Merged
osmontero merged 6 commits into
utmstack:v11from
kryonsx:codex/v11-crowdstrike-review-20260924
Sep 25, 2026
Merged

osmontero merged 6 commits into
utmstack:v11from
kryonsx:codex/v11-crowdstrike-review-20260924

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Five of the seventeen CrowdStrike rules test a field that this integration can never deliver, so they can never fire. Eight rules group or deduplicate alerts by fields that do not exist on an alert, and the filter damages quoted command lines.

This draft fixes those. It deliberately leaves the outcome block and the sign-in address mapping to the separate outcome draft #2680. The two drafts merge cleanly, and both test sets pass together.

Built and tested on the latest versions: v11 d2479c1a, go-sdk v1.1.36, and EventProcessor 8a3ade7. That engine revision is the same source as today's eventprocessor:v11.2.14 image and eventprocessor/base:1.1.7.

Predecessor: #2602 (closed, historical context only). Related: #2680 (outcome-only, separate campaign).

Evidence (read-only, described without identifying data)

  • One pre-release instance holds CrowdStrike data: the Falcon event stream of one account, retained since 2026-09-21, about 1,170 records.
  • It contains four event types:
    • APIActivityAuditEvent;
    • AuditLogV3Event (a second, nested copy of each audit record);
    • UserActivityAuditEvent;
    • AuthActivityAuditEvent.
  • There are no detection, incident, real-time-response, custom-indicator or process records.
  • The deployed filter and rules match this repository.
  • No CrowdStrike rule has ever raised an alert or a circuit-breaker alert on that instance.
  • Falcon's event-stream field reference sits behind the console login and was not available. Every change rests on the plugin, filter, engine and go-sdk code plus the real records.

Filter (filters/crowdstrike/crowdstrike.yml)

  • Quoted command lines: removes the two steps that strip one double quote from each end of log.eventCommandLine. They left quoted Windows paths and arguments unbalanced; for example, "C:\Tools\x.exe" -a "C:\Temp\y.dmp" was stored as C:\Tools\x.exe" -a "C:\Temp\y.dmp.
  • Dead renames: removes three renames that repeat earlier ones and can never find their source.
  • Version comment: kept at 1.2.0 so this draft does not conflict with fix(crowdstrike): explicit final outcome and sign-in client address #2680 on line 1.

Rules

  • inhibit_system_recovery, os_credential_dumping_activity, suspicious_encoded_powershell_execution, suspicious_native_downloaders, windows_event_log_clearing:
    • remove equals("log.event_simpleName", "ProcessRollup2");
    • event_simpleName is a Data Replicator field; the plugin forwards only event-stream records (metadata and event), and no filter step writes it.
  • Seven rules: groupBy uses adversary.host/adversary.user instead of origin.*. An alert has no origin, so these keys were always skipped.
  • Brute-force rule: deduplicateBy: adversary.ip instead of origin.ip. Before, every failure after the threshold raised another alert.
  • Names, conditions otherwise, thresholds, windows, history searches and MITRE labels are unchanged.

Validation

  • Playground (engine 8a3ade7), filter:
    • 63 real records (private) and fabricated records gave 0 errors.
    • All real records are identical before and after.
    • The original filter altered 7 of 8 fabricated command lines; the new one keeps all 8.
  • Playground (engine 8a3ade7), rules:
    • The five command-line rules alert only on the 14 fabricated positives.
    • They alert on no near miss and no real record, with no circuit breakers or condition errors.
    • The new grouping keys resolve on all 65 alerts of the regrouped rules (alerts plugin grouping.go).
    • The brute-force history query was attempted without OpenSearch, so live history remains untested; it was checked against a local mock.
  • go-sdk v1.1.36 replay: the five rules go from 0 matches to their positives, with 0 real-record matches.
  • Go tests:

Deferred

These need Falcon documentation, real records or an owner decision:

  • the AuditLogV3Event copies (drop, pass through or map). If they were ever normalized, each failure would count twice in the brute-force history.
  • actor and target user fields;
  • the command-line location and the severity scale;
  • platform and operation names;
  • the meaning of PolicyDisabled;
  • outcome values (fix(crowdstrike): explicit final outcome and sign-in client address #2680);
  • MITRE relabelling for techniques retired in ATT&CK v19. This should be one repository-wide pass: T1562 appears in 66 rules and "Defense Evasion" in 132.

Operational notes (not code changes)

  • The plugin keeps stream offsets only in memory, so events created while it is down are skipped (plugins/crowdstrike/main.go).
  • On the reviewed instance, a second API client fails every stream refresh with "not found". It should be fixed or revoked at the source.

See filters/audits/crowdstrike.md.

🤖 Generated with Claude Code

kryonsx and others added 6 commits September 24, 2026 19:06
The two trim steps that removed a double quote from the start and from the
end of log.eventCommandLine each strip one quote after trimming spaces
(EventProcessor plugins/trim). A Windows command line that starts with a
quoted program path or ends with a quoted argument therefore lost one quote
of a pair and was stored with unbalanced quotes. The stored text no longer
matched what ran, and three rules group alerts by it. The parent and
grandparent command lines were never trimmed.

Only these two steps are removed. The '[{' and '}]' trims stay, and they
still remove surrounding spaces. No retained real record carries a command
line, so the proof uses fabricated detection records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
log.event.Attributes.trace_id, log.event.ServiceName and log.event.Message
were each renamed twice to the same target. The first rename moves the value
and no step in between recreates the source, so the second one never finds
anything. On the EventProcessor 8a3ade7 playground every sampled real record
is identical before and after.

The version comment on line 1 stays at 1.2.0: draft utmstack#2680 changes that line,
and whichever draft merges second takes the next version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ne rules

The CrowdStrike plugin forwards Falcon event-stream records whose only
top-level keys are metadata and event (plugins/crowdstrike/main.go,
processEvent), and no filter step writes log.event_simpleName, a Falcon
Data Replicator field. The condition could never be true, so these five
rules never fired. Without it they match detection summaries whose command
line matches their patterns.

Rules: inhibit_system_recovery, os_credential_dumping_activity,
suspicious_encoded_powershell_execution, suspicious_native_downloaders and
windows_event_log_clearing. Names, patterns, impact and MITRE labels are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An alert has no origin. For rules with adversary: origin the CEL plugin
copies the event's origin into the alert's adversary, and plugins/alerts
grouping.go skips keys it cannot resolve. So origin.host and origin.user
in groupBy were always skipped: three rules never grouped, and four grouped
only by their lastEvent key, which merged alerts from different hosts. The
brute-force rule's deduplicateBy origin.ip never deduplicated.

Seven rules now group by adversary.host (and adversary.user where they used
origin.user), and the brute-force rule deduplicates by adversary.ip. Its
condition, threshold, window and history search are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crowdstrike_review_test.go runs 18 fabricated Falcon event-stream records
(testdata/crowdstrike-review/raw.json) through a model of the EventProcessor
8a3ade7 step plugins, with every where clause evaluated by go-sdk v1.1.36.
The model gave the same fields as the real 8a3ade7 playground on every
record tried. The tests check that:

- command lines are stored as sent, apart from surrounding spaces;
- no unconditional rename repeats an earlier one;
- each record matches exactly the listed rules, and every field a rule
  reads is one the filter writes;
- every groupBy and deduplicateBy key is an alert field that resolves,
  through grouping.go, on the alert the CEL plugin would build.

All four tests fail on the original filter and rules. Identifiers and
file names differ from draft utmstack#2680's, so both drafts compile together.
The records carry no UserIp and assert no actionResult, which belong to
utmstack#2680.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
filters/audits/crowdstrike.md states what the corrections rest on (the
CrowdStrike plugin, EventProcessor 8a3ade7, grouping.go and go-sdk v1.1.36,
with genuine records read privately and vendor documentation unavailable),
each change with its proof, the relationship with draft utmstack#2680 (no shared
lines; the version comment stays at 1.2.0 so the drafts do not conflict),
the deferred items including the MITRE ATT&CK v19 relabel and the
AuditLogV3Event double-count risk, an operational note on a failing API
client, and the limits of the proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@osmontero
osmontero marked this pull request as ready for review September 25, 2026 15:21
@osmontero
osmontero requested a review from a team September 25, 2026 15:21
@osmontero
osmontero merged commit a8b6788 into utmstack:v11 Sep 25, 2026
3 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants