Skip to content

fix: Splunk Add-On merges distinct incidents due to UUID collision with imprecise event timestamps #44

Description

@fruitcakej

We have identified a critical issue with the OpenCTI Splunk Add-On where it incorrectly updates existing OpenCTI incidents instead of creating new ones for distinct Splunk security events. This occurs when multiple Splunk events trigger the same use case at the exact same second, leading to a collision in the generated STIX Incident ID.

This behavior results in data loss, corruption, and incorrect incident tracking, especially prevalent in high-volume environments like MSSPs where multiple similar alerts can fire concurrently.

Affected Components & Versions

  • OpenCTI Splunk Add-On: Version 1.1.6 (and likely earlier/later versions using the same ID generation logic)
  • OpenCTI Platform: Version 6.9.28 (behavior is likely platform-agnostic regarding STIX ID updates)

Steps to Reproduce

  1. Configure Splunk Add-On: Ensure the Splunk Add-On for OpenCTI is configured to create incidents based on Splunk alerts.
  2. Trigger Use Case (Event A): A Splunk use case is triggered for Customer A at a specific time (e.g., _time=1771487340 (epoch without milliseconds), name="Malicious IP Observed").
  3. OpenCTI Incident Created: A new case/incident is created in OpenCTI for Customer A. (Optionally, a custom connector might trigger to add a short ID to the title).
  4. Trigger Use Case (Event B): A different, distinct Splunk event for Customer B (or even a separate event for Customer A) triggers the same use case (name="Malicious IP Observed") at the exact same second as Event A (_time=1771487340).
  5. Observe Incorrect Behavior: Instead of creating a new incident for Customer B, the Splunk Add-On updates the existing incident (originally created for Customer A) with information from Event B.

Expected Behavior

For each distinct Splunk security event that triggers an OpenCTI incident creation, a new and unique OpenCTI incident should be created, regardless of whether other events with the same use case name occur within the same second.

Actual Behavior

When name (Splunk use case name) and created (derived from Splunk's _time field, lacking millisecond precision) are identical for two or more distinct Splunk events, the Splunk Add-On generates the same STIX Incident ID. OpenCTI, upon receiving an object with an existing STIX ID, performs an update operation, leading to the merging of distinct security incidents.

Root Cause Analysis

The issue stems from the deterministic generation of the STIX Incident ID using uuid.uuid5 in the Splunk Add-On's utils.py.

Relevant Code Snippets:

ta_opencti_add_on/stix_converter.py` (line 143, or similar):**

stix_case_incident = CustomObjectCaseIncident(
    id=generate_case_incident_id(alert_params.get("name"), event_date), # <--- ID generated here
    name=alert_params.get("name"),
    description=alert_params.get("description"),
    severity=alert_params.get("severity"),
    priority=alert_params.get("priority"),
    labels=alert_params.get("labels"),
    created=event_date,
    external_references=[],
    created_by_ref=stix_author.id,
    object_marking_refs=[marking_id],
    object_refs=observable_ref_ids
)

ta_opencti_add_on/utils.py (line 143, or similar):

def generate_incident_id(name: str, created):
    """
    :param name:
    :param created:
    :return:
    """
    if isinstance(created, datetime.datetime):
        created = created.isoformat() # <--- Converts to ISO format, but _time often lacks milliseconds
    data = {"name": name.lower().strip(), "created": created}
    data = canonicalize(data, utf8=False)
    entity_id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data)) # <--- Deterministic UUID
    return "incident--" + entity_id

The name parameter typically corresponds to the Splunk alert/use case name, which can be identical across many events. The created parameter is derived from Splunk's _time field, which is often an epoch timestamp without millisecond precision. Consequently, if two distinct events share the same name and occur within the same second, the data dictionary passed to uuid.uuid5 will be identical, leading to the generation of the exact same STIX ID (incident--...).
Impact

Data Integrity Issues: Information from separate security events is merged, leading to an incomplete or inaccurate representation of each incident.
Operational Inefficiency: SOC/CSIRT analysts might miss critical details or misinterpret incidents due to merged data.
Misleading Metrics: Incident counts and resolution times can be skewed.
Compliance Risk: Inability to accurately track and report on distinct security events.

Proposed Solutions / Enhancements

  1. Introduce a Configuration Option for Incident Creation Strategy:
    Add a new configuration parameter to the Splunk Add-On (e.g., INCIDENT_CREATION_STRATEGY) that allows users to define the desired behavior:
    ALWAYS_CREATE_NEW (default/recommended for most use cases)
    UPSERT (current behavior, for specific scenarios where updating by name+time is desired)

  2. Modify generate_incident_id for Unique ID Generation (for ALWAYS_CREATE_NEW strategy):
    When the strategy is ALWAYS_CREATE_NEW, the generate_incident_id function must incorporate a truly unique identifier from the Splunk event to ensure a unique STIX ID for each distinct event.

Option A (Splunk Event ID): If Splunk provides a unique event ID (e.g., _id field in some contexts), this should be included in the data dictionary for uuid.uuid5.
Option B (High-Precision Timestamp + Random Salt): If a unique Splunk event ID is not reliably available or accessible, modify the function to include a high-precision timestamp (if available from Splunk, e.g., _time with microseconds if possible to extract) combined with a randomly generated UUID or string.

`import uuid
import datetime

def generate_incident_id_unique(name: str, created: datetime.datetime, splunk_event_id: str = None):
if isinstance(created, datetime.datetime):
# Use ISO format with microseconds for better granularity
created_str = created.isoformat(timespec='microseconds')
else:
created_str = created

data = {"name": name.lower().strip(), "created": created_str}

if splunk_event_id:
    data["splunk_event_id"] = splunk_event_id
else:
    # Fallback to a random UUID if no specific Splunk ID is available,
    # ensuring uniqueness even if name and created_str are identical
    data["unique_salt"] = str(uuid.uuid4())

# Ensure canonicalization for consistent UUID5 generation
data = canonicalize(data, utf8=False)
entity_id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
return "incident--" + entity_id`

ref: OCTI2-1984

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugType: something isn't working (fix:).

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions