From c7569fd4d0bf038b069e25b67d6e20ee31db0b32 Mon Sep 17 00:00:00 2001 From: Guillaume BOURGEOIS Date: Tue, 8 Sep 2026 10:21:27 +0200 Subject: [PATCH] fix(rapid7-insightidr): map both ends of a connection (v1.7.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An "RDP connection to DC" investigation arrived with device {}, process {} and two indexed entities, both of them false — the 32-hex tails of its own alert RRNs, which the IOC regex cannot tell from an MD5. Its four real values were sitting in the payload, unmapped: source_account vbalbakakis destination_account vassilisadmin source_asset_address sgazswvdip-01.toto.priv destination_asset_address scazsvpdc01.toto.priv The mappings assumed an alert has one host and one user, which holds for a process_start_event and not for an auth rule: a connection names both ends and neither is "the host". So source and destination now land on src_endpoint and dst_endpoint, the acting account on actor.user and the account acted upon on user — the distinction that lets a lateral move be read in one line. That choice is also what makes the fields placeable. The layout builder's field picker is a closed over + # the OCSF catalogue. A path that is not in that catalogue cannot be placed on + # a layout at all, which is why `detection` below, useful as it is, only ever + # shows up in the Context tab. + - { source_path: "alerts[0].rule_keys_of_interest[key='source_asset_address'].values[0]", ocsf_field: "src_endpoint.hostname" } + - { source_path: "alerts[0].rule_keys_of_interest[key='destination_asset_address'].values[0]", ocsf_field: "dst_endpoint.hostname" } + - { source_path: "alerts[0].rule_keys_of_interest[key='destination_asset'].values[0]", ocsf_field: "dst_endpoint.hostname" } + # actor.user is who acted, user is who it was done TO. Keeping them apart is + # what lets an analyst read a lateral move in one line. + - { source_path: "alerts[0].rule_keys_of_interest[key='source_account'].values[0]", ocsf_field: "actor.user.name" } + - { source_path: "alerts[0].rule_keys_of_interest[key='destination_account'].values[0]", ocsf_field: "user.name" } # ── Every key the rule found interesting ────────────────────────── # rule_keys_of_interest is an OPEN vocabulary: each detection rule names its own # keys. A process_start_event rule emits hostname / process.cmd_line / @@ -137,12 +153,14 @@ ocsf: "time": alerted_at, "status": status, "disposition": disposition, - "device": { "hostname": rule_keys_of_interest[key='hostname'].values[0] }, - "process": { - "name": rule_keys_of_interest[key='process.name'].values[0], - "cmd_line": rule_keys_of_interest[key='process.cmd_line'].values[0] - }, - "attacks": { "technique": { "uid": rule.mitre_tcodes[$substring($, 0, 2) != 'TA'][0] } }, + "hostname": rule_keys_of_interest[key='hostname'].values[0], + "src_hostname": rule_keys_of_interest[key='source_asset_address'].values[0], + "dst_hostname": rule_keys_of_interest[key='destination_asset_address'].values[0], + "actor_user": rule_keys_of_interest[key='source_account'].values[0], + "target_user": rule_keys_of_interest[key='destination_account'].values[0], + "process": rule_keys_of_interest[key='process.name'].values[0], + "cmd_line": rule_keys_of_interest[key='process.cmd_line'].values[0], + "technique": rule.mitre_tcodes[$substring($, 0, 2) != 'TA'][0], "detection": rule_keys_of_interest{key: values[0]} } ocsf_field: "evidences" diff --git a/integrations/rapid7-insightidr/mappers/search_alerts.yaml b/integrations/rapid7-insightidr/mappers/search_alerts.yaml index 036ce89..1e9c4fe 100644 --- a/integrations/rapid7-insightidr/mappers/search_alerts.yaml +++ b/integrations/rapid7-insightidr/mappers/search_alerts.yaml @@ -124,6 +124,22 @@ ocsf: - { source_path: "investigation.priority", ocsf_field: "finding_info.related_events.priority" } - { source_path: "investigation.disposition", ocsf_field: "finding_info.related_events.disposition" } - { source_path: "investigation.rrn", ocsf_field: "finding_info.related_events.uid" } + # ── Lateral movement: source and destination ────────────────────── + # An asset_auth rule describes a connection, so it names BOTH ends and neither + # is "the host": source_asset_address / destination_asset_address, and + # source_account / destination_account. Mapping them to src_endpoint and + # dst_endpoint is not only the correct OCSF shape — it is also what makes them + # appear in the layout builder, whose field picker is a closed