Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/crowdstrike/changelog.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# newer versions go on top
- version: "4.5.0"
changes:
- description: Map CommandHistory events to ECS process fields.
type: enhancement
link: https://github.com/elastic/integrations/pull/20285
- version: "4.4.0"
changes:
- description: >-
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"event_simpleName":"CommandHistory","CommandHistory":"sc stop CSFalconService¶Stop-Service -Name CSFalconService -Force","FirstCommand":"0","CommandCountMax":"2","CommandCount":"2","CommandSequence":"1","LastDisplayed":"0","LastAdded":"0","TargetProcessId":"7292754410393","AuthenticationId":"2048391741","ConfigStateHash":"886310903","EffectiveTransmissionClass":"3","Entitlements":"15","EventOrigin":"1","LogonType":"10","UserName":"jdoe","aip":"81.2.69.144","aid":"b5c2f3e1d4a789102345678abcdef01","timestamp":"1752051389000","cid":"a1b2c3d4e5f67890abcdef1234567890","id":"a1b2c3d4e5f67890abcdef1234567890a1b2","event_platform":"Win","name":"CommandHistoryV5","ConfigBuild":"1007.3.0021003.10"}
{"event_simpleName":"CommandHistory","CommandHistory":"sc stop CSFalconService","FirstCommand":"0","CommandCountMax":"1","CommandCount":"1","CommandSequence":"1","LastDisplayed":"0","LastAdded":"0","TargetProcessId":"7292754410394","AuthenticationId":"2048391742","ConfigStateHash":"886310903","EffectiveTransmissionClass":"3","Entitlements":"15","EventOrigin":"1","LogonType":"2","UserName":"jdoe","aip":"81.2.69.144","aid":"b5c2f3e1d4a789102345678abcdef01","timestamp":"1752051390000","cid":"a1b2c3d4e5f67890abcdef1234567890","id":"b2c3d4e5f67890abcdef1234567890b2c3","event_platform":"Win","name":"CommandHistoryV5","ConfigBuild":"1007.3.0021003.10"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
{
"expected": [
{
"@timestamp": "2025-07-09T08:56:29.000Z",
"crowdstrike": {
"AuthenticationId": "2048391741",
"CommandCount": 2,
"CommandCountMax": 2,
"CommandHistory": [
"sc stop CSFalconService",
"Stop-Service -Name CSFalconService -Force"
],
"CommandSequence": "1",
"ConfigStateHash": "886310903",
"EffectiveTransmissionClass": "3",
"Entitlements": "15",
"EventOrigin": "1",
"FirstCommand": "0",
"LastAdded": "0",
"LastDisplayed": "0",
"LogonType": "10",
"cid": "a1b2c3d4e5f67890abcdef1234567890",
"event_simpleName": "CommandHistory",
"id": "a1b2c3d4e5f67890abcdef1234567890a1b2",
"name": "CommandHistoryV5"
},
"device": {
"id": "b5c2f3e1d4a789102345678abcdef01"
},
"event": {
"action": "CommandHistory",
"category": [
"process"
],
"created": "2025-07-09T08:56:29.000Z",
"id": "a1b2c3d4e5f67890abcdef1234567890a1b2|b5c2f3e1d4a789102345678abcdef01|a1b2c3d4e5f67890abcdef1234567890",
"kind": "event",
"original": "{\"event_simpleName\":\"CommandHistory\",\"CommandHistory\":\"sc stop CSFalconService¶Stop-Service -Name CSFalconService -Force\",\"FirstCommand\":\"0\",\"CommandCountMax\":\"2\",\"CommandCount\":\"2\",\"CommandSequence\":\"1\",\"LastDisplayed\":\"0\",\"LastAdded\":\"0\",\"TargetProcessId\":\"7292754410393\",\"AuthenticationId\":\"2048391741\",\"ConfigStateHash\":\"886310903\",\"EffectiveTransmissionClass\":\"3\",\"Entitlements\":\"15\",\"EventOrigin\":\"1\",\"LogonType\":\"10\",\"UserName\":\"jdoe\",\"aip\":\"81.2.69.144\",\"aid\":\"b5c2f3e1d4a789102345678abcdef01\",\"timestamp\":\"1752051389000\",\"cid\":\"a1b2c3d4e5f67890abcdef1234567890\",\"id\":\"a1b2c3d4e5f67890abcdef1234567890a1b2\",\"event_platform\":\"Win\",\"name\":\"CommandHistoryV5\",\"ConfigBuild\":\"1007.3.0021003.10\"}",
"outcome": "success",
"type": [
"end",
"info"
]
},
"host": {
"id": "b5c2f3e1d4a789102345678abcdef01",
"os": {
"type": "windows"
}
},
"observer": {
"address": [
"81.2.69.144"
],
"geo": {
"city_name": "London",
"continent_name": "Europe",
"country_iso_code": "GB",
"country_name": "United Kingdom",
"location": {
"lat": 51.5142,
"lon": -0.0931
},
"region_iso_code": "GB-ENG",
"region_name": "England"
},
"ip": [
"81.2.69.144"
],
"serial_number": "b5c2f3e1d4a789102345678abcdef01",
"version": "1007.3.0021003.10"
},
"process": {
"command_line": "sc stop CSFalconService¶Stop-Service -Name CSFalconService -Force",
Comment thread
efd6 marked this conversation as resolved.
Outdated
"entity_id": "7292754410393",
"interactive": true,
"user": {
"name": "jdoe"
}
},
"related": {
"hash": [
"886310903"
],
"ip": [
"81.2.69.144"
],
"user": [
"jdoe"
]
},
"tags": [
"preserve_original_event"
],
"user": {
"name": "jdoe"
}
},
{
"@timestamp": "2025-07-09T08:56:30.000Z",
"crowdstrike": {
"AuthenticationId": "2048391742",
"CommandCount": 1,
"CommandCountMax": 1,
"CommandHistory": [
"sc stop CSFalconService"
],
"CommandSequence": "1",
"ConfigStateHash": "886310903",
"EffectiveTransmissionClass": "3",
"Entitlements": "15",
"EventOrigin": "1",
"FirstCommand": "0",
"LastAdded": "0",
"LastDisplayed": "0",
"LogonType": "2",
"cid": "a1b2c3d4e5f67890abcdef1234567890",
"event_simpleName": "CommandHistory",
"id": "b2c3d4e5f67890abcdef1234567890b2c3",
"name": "CommandHistoryV5"
},
"device": {
"id": "b5c2f3e1d4a789102345678abcdef01"
},
"event": {
"action": "CommandHistory",
"category": [
"process"
],
"created": "2025-07-09T08:56:30.000Z",
"id": "b2c3d4e5f67890abcdef1234567890b2c3|b5c2f3e1d4a789102345678abcdef01|a1b2c3d4e5f67890abcdef1234567890",
"kind": "event",
"original": "{\"event_simpleName\":\"CommandHistory\",\"CommandHistory\":\"sc stop CSFalconService\",\"FirstCommand\":\"0\",\"CommandCountMax\":\"1\",\"CommandCount\":\"1\",\"CommandSequence\":\"1\",\"LastDisplayed\":\"0\",\"LastAdded\":\"0\",\"TargetProcessId\":\"7292754410394\",\"AuthenticationId\":\"2048391742\",\"ConfigStateHash\":\"886310903\",\"EffectiveTransmissionClass\":\"3\",\"Entitlements\":\"15\",\"EventOrigin\":\"1\",\"LogonType\":\"2\",\"UserName\":\"jdoe\",\"aip\":\"81.2.69.144\",\"aid\":\"b5c2f3e1d4a789102345678abcdef01\",\"timestamp\":\"1752051390000\",\"cid\":\"a1b2c3d4e5f67890abcdef1234567890\",\"id\":\"b2c3d4e5f67890abcdef1234567890b2c3\",\"event_platform\":\"Win\",\"name\":\"CommandHistoryV5\",\"ConfigBuild\":\"1007.3.0021003.10\"}",
"outcome": "success",
"type": [
"end",
"info"
]
},
"host": {
"id": "b5c2f3e1d4a789102345678abcdef01",
"os": {
"type": "windows"
}
},
"observer": {
"address": [
"81.2.69.144"
],
"geo": {
"city_name": "London",
"continent_name": "Europe",
"country_iso_code": "GB",
"country_name": "United Kingdom",
"location": {
"lat": 51.5142,
"lon": -0.0931
},
"region_iso_code": "GB-ENG",
"region_name": "England"
},
"ip": [
"81.2.69.144"
],
"serial_number": "b5c2f3e1d4a789102345678abcdef01",
"version": "1007.3.0021003.10"
},
"process": {
"command_line": "sc stop CSFalconService",
"entity_id": "7292754410394",
"interactive": true,
"user": {
"name": "jdoe"
}
},
"related": {
"hash": [
"886310903"
],
"ip": [
"81.2.69.144"
],
"user": [
"jdoe"
]
},
"tags": [
"preserve_original_event"
],
"user": {
"name": "jdoe"
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8066,6 +8066,7 @@
},
"process": {
"entity_id": "816054990879",
"interactive": false,
"thread": {
"id": 52913017705957
}
Expand Down Expand Up @@ -8553,6 +8554,7 @@
},
"process": {
"entity_id": "7073822473144",
"interactive": false,
"pid": 744,
"thread": {
"id": 48689911139327
Expand Down Expand Up @@ -9365,6 +9367,7 @@
},
"process": {
"entity_id": "30254389526587",
"interactive": false,
"thread": {
"id": 275230771323179
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@
"ApplicationName": "7371f07",
"CommandCount": 3,
"CommandCountMax": 983,
"CommandHistory": "41c56f9bd5471921f1ad5eb1eaa88bd7d4dcce1feb86682fa",
"CommandHistory": [
"41c56f9bd5471921f1ad5eb1eaa88bd7d4dcce1feb86682fa"
],
"ConfigStateHash": "7040662006",
"EffectiveTransmissionClass": "3",
"Entitlements": "96",
Expand Down Expand Up @@ -448,6 +450,7 @@
"version": "2c8b.4.bab7095.e6"
},
"process": {
"command_line": "41c56f9bd5471921f1ad5eb1eaa88bd7d4dcce1feb86682fa",
"entity_id": "3502200260267"
},
"related": {
Expand Down Expand Up @@ -6223,6 +6226,7 @@
"command_line": "82\\340d600\\4a6a0a4b\\4cdbc.exe",
"entity_id": "25700434000",
"executable": "\\6ba0bd\\257d4e13\\d598026\\dc9ec376\\4cdbc.exe",
"interactive": false,
"name": "4cdbc.exe"
},
"related": {
Expand Down Expand Up @@ -8749,6 +8753,7 @@
},
"process": {
"entity_id": "6990300009751",
"interactive": false,
"thread": {
"id": 95004910750900
}
Expand Down Expand Up @@ -8955,6 +8960,7 @@
},
"process": {
"entity_id": "3014546000418",
"interactive": false,
"pid": 173,
"thread": {
"id": 610009300097503
Expand Down Expand Up @@ -9144,6 +9150,7 @@
},
"process": {
"entity_id": "1776052010570",
"interactive": false,
"thread": {
"id": 500010382010308
}
Expand Down Expand Up @@ -11942,6 +11949,7 @@
"command_line": "System",
"entity_id": "21475403566",
"executable": "System",
"interactive": true,
"name": "System"
},
"related": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,42 @@ processors:

ctx.process.args = commandLineToArgv(ctx.process.command_line);
ctx.process.args_count = ctx.process.args.length;
- script:
description: >-
Map the CommandHistory field to process.command_line and split into an
array at crowdstrike.CommandHistory. CrowdStrike uses U+00B6 PILCROW
SIGN (¶) as the delimiter between individual commands in the history
string; this character has no formal documentation from CrowdStrike but
is consistently observed in FDR CommandHistory events.
tag: script_command_history_to_process_command_line_8c3e1f92
if: |-
ctx.crowdstrike?.event_simpleName == 'CommandHistory' &&
ctx.crowdstrike?.CommandHistory instanceof String &&
ctx.crowdstrike.CommandHistory != ''
source: |-
def history = ctx.crowdstrike.CommandHistory;
ctx.process = ctx.process ?: [:];
ctx.process.command_line = history;
def parts = history.splitOnToken('¶');
def commands = new ArrayList();
for (def cmd : parts) {
def trimmed = cmd.trim();
if (!trimmed.isEmpty()) {
commands.add(trimmed);
}
}
ctx.crowdstrike.CommandHistory = commands;
on_failure:
- append:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🔵 Low confidence: high path: packages/crowdstrike/data_stream/fdr/elasticsearch/ingest_pipeline/default.yml:1085

The on_failure append processors added by this PR have no tag and use an ad-hoc error message instead of the standard ingest.on_failure* template every other handler in this pipeline uses; align them with the existing convention.

Details

Both new script processors (script_command_history_to_process_command_line_8c3e1f92 here, and script_process_interactive_logon_type_9b3d6c7a further down) attach an on_failure append that omits the tag field and writes a custom string carrying only _ingest.on_failure_message.

Every other on_failure handler in this pipeline uses a tagged append with the full template that also records the failing processor type, tag and pipeline, for example append_error_message_4ef54c75 on the json processor. Without those fields an error.message from these handlers cannot be traced back to the processor that produced it, and the untagged processors will be flagged by elastic-package check once the package moves to format_version 3.6.0 or later.

Recommendation:

Use the pipeline's standard tagged handler on both new scripts:

      on_failure:
        - append:
            tag: append_error_message_8c3e1f92
            field: error.message
            value: 'Processor {{{_ingest.on_failure_processor_type}}} with tag {{{_ingest.on_failure_processor_tag}}} in pipeline {{{_ingest.on_failure_pipeline}}} failed with message: {{{_ingest.on_failure_message}}}'

🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

field: error.message
value: "Failed to parse CommandHistory: {{{_ingest.on_failure_message}}}"
- set:
tag: set_process_user_name_command_history_4e2a8f1c
description: Map crowdstrike.UserName to process.user.name for CommandHistory events.
if: ctx.crowdstrike?.event_simpleName == 'CommandHistory' && ctx.crowdstrike?.UserName instanceof String && ctx.crowdstrike.UserName != ''
field: process.user.name
copy_from: crowdstrike.UserName
ignore_empty_value: true
- rename:
tag: rename_crowdstrike_ImageFileName_to_process_executable_1e9d3140
if: ctx._temp?.isLibrary != true && ctx._temp?.isDriver != true
Expand Down Expand Up @@ -3150,6 +3186,31 @@ processors:
}
}

- script:
tag: script_process_interactive_logon_type_9b3d6c7a
description: >-
Set process.interactive from crowdstrike.LogonType whenever a process
object is present in the event. Placed after all other process
processors so that any processor that creates a process object
(TargetProcessId, ContextProcessId, etc.) has already run. Logon types
2 (Interactive), 10 (RemoteInteractive), 11 (CachedInteractive), and 12
(CachedRemoteInteractive) indicate an interactive shell session; all
other types are non-interactive.
if: >-
ctx.crowdstrike?.LogonType instanceof String &&
ctx.crowdstrike.LogonType != '' &&
ctx.process != null
source: |-
// Logon type reference: 2=Interactive, 3=Network, 4=Batch, 5=Service,
// 7=Unlock, 8=NetworkCleartext, 9=NewCredentials, 10=RemoteInteractive,
// 11=CachedInteractive, 12=CachedRemoteInteractive, 13=CachedUnlock
def logonType = ctx.crowdstrike.LogonType;
ctx.process.interactive = (logonType == '2' || logonType == '10' || logonType == '11' || logonType == '12');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🟠 High confidence: medium path: packages/crowdstrike/data_stream/fdr/elasticsearch/ingest_pipeline/default.yml:3258

process.interactive is derived from crowdstrike.LogonType on every event that has any process object, which labels processes that are not the session's shell — restrict the processor to CommandHistory events.

Details

LogonType is an attribute of the Windows logon session, but ECS defines process.interactive per-process ("inferred from the processes file descriptors... the character device for the controlling tty is the same as stdin and stderr") — the definition this PR adds to docs/README.md line 3197. Applying the session's LogonType to whatever process object happens to be on the event produces wrong values, and the PR's own fixtures show it:

  • packages/crowdstrike/data_stream/fdr/_dev/test/pipeline/test-windows.log-expected.json line 11952 now asserts "interactive": true for the Windows System process (event_simpleName LogonBehaviorCompositionDetectInfo, ImageFileName/CommandLine "System", entity_id 21475403566). System is the kernel process; it has no controlling tty and can never be attached to an interactive shell. It is marked interactive purely because the session's LogonType is 2.
  • test-fdr.log-expected.json lines 8069, 8557, 9370 and test-windows.log-expected.json lines 8756, 8963, 9153 add "interactive": false to UserLogon/authentication events whose process object is only a ContextProcessId reference (entity_id plus thread.id). ECS false means "known to be non-interactive"; here interactivity is simply unknown, so the pipeline is asserting a fact the source event does not carry.

The ctx.process != null gate added in this revision keeps the field off events with no process object at all, but it does not distinguish the process the logon session actually belongs to from an unrelated context/target process reference, so both cases above still fire.

Recommendation:

Scope the processor to the event type this PR is about (CommandHistory), where the recorded shell genuinely is the process the logon session belongs to:

  - script:
      tag: script_process_interactive_logon_type_9b3d6c7a
      description: >-
        Set process.interactive from crowdstrike.LogonType for CommandHistory
        events only. On other FDR events LogonType describes the logon session,
        not the process referenced by ContextProcessId/TargetProcessId, so the
        value would not describe that process. Logon types 2 (Interactive),
        10 (RemoteInteractive), 11 (CachedInteractive) and 12
        (CachedRemoteInteractive) indicate an interactive shell session.
      if: >-
        ctx.crowdstrike?.event_simpleName == 'CommandHistory' &&
        ctx.crowdstrike?.LogonType instanceof String &&
        ctx.crowdstrike.LogonType != '' &&
        ctx.process != null
      source: |-
        def logonType = ctx.crowdstrike.LogonType;
        ctx.process.interactive = (logonType == '2' || logonType == '10' || logonType == '11' || logonType == '12');
      on_failure:
        - append:
            field: error.message
            value: "Failed to set process.interactive from LogonType: {{{_ingest.on_failure_message}}}"

This reverts the added "interactive" lines in test-fdr.log-expected.json and test-windows.log-expected.json and keeps only the two CommandHistory assertions in test-fdr-command-history.log-expected.json.


🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's intentional. We want to map process.interactive in every event it contains crowdstrike.LogonType and contains a process object.

on_failure:
- append:
field: error.message
value: "Failed to set process.interactive from LogonType: {{{_ingest.on_failure_message}}}"

# Cleanup.
- remove:
tag: remove_crowdstrike_event_platform_f15993d1
Expand Down
4 changes: 4 additions & 0 deletions packages/crowdstrike/data_stream/fdr/fields/ecs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@
name: process.hash.md5
- external: ecs
name: process.hash.sha256
- external: ecs
name: process.interactive
- external: ecs
name: process.parent.entity_id
- external: ecs
Expand All @@ -179,6 +181,8 @@
name: process.title
- external: ecs
name: process.uptime
- external: ecs
name: process.user.name
- external: ecs
name: related.hash
- external: ecs
Expand Down
2 changes: 2 additions & 0 deletions packages/crowdstrike/data_stream/fdr/fields/fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@
multi_fields:
- name: text
type: match_only_text
- name: CommandSequence
type: match_only_text
Comment thread
navnit-elastic marked this conversation as resolved.
Outdated

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: 🔵 Low confidence: medium path: packages/crowdstrike/data_stream/fdr/fields/fields.yml:233

crowdstrike.CommandSequence is declared as match_only_text, so the sequence index cannot be filtered exactly, sorted or aggregated — declare it as keyword.

Details

CommandSequence carries the position of the entry within the CrowdStrike command-history buffer ("1" in both events of test-fdr-command-history.log). match_only_text is a text type: it is not doc_values-backed, so the field cannot be used in a terms aggregation, cannot be sorted on, and cannot be matched with an exact term query — all of which are the natural uses of a sequence index when reconstructing command order.

The adjacent CommandCount and CommandCountMax entries (fields.yml lines 221-226) are also match_only_text, but each sits above a commented-out type: long declaration, which marks them as a known legacy compromise rather than the convention to follow for a newly added field.

Recommendation:

Declare the sequence index as a keyword:

    - name: CommandSequence
      type: keyword

The docs/README.md row for crowdstrike.CommandSequence needs regenerating with elastic-package build after the change.


🤖 AI-Generated Review | Vera Review Bot | 📚 Knowledge base: integration-skills

⚠️ Automated review — verify suggestions before applying.

- name: CompletionEventId
type: keyword
- name: CompositeId
Expand Down
Loading
Loading