Detection rules › Elastic

Alerts in Different ATT&CK Tactics by Host

Status
production
Severity
high
Time window
8h
Group by
host.id
Author
Elastic
Source
github.com/elastic/detection-rules

This rule correlates medium-or-higher severity alerts involving the same host from at least two distinct detection rules mapped to three or more ATT&CK tactics. Analysts can use this to prioritize triage and response, as this combination may indicate host compromise.

Rule body

[metadata]
creation_date = "2025/11/19"
maturity = "production"
updated_date = "2026/07/21"

[rule]
author = ["Elastic"]
description = """
This rule correlates medium-or-higher severity alerts involving the same host from at least two distinct detection rules
mapped to three or more ATT&CK tactics. Analysts can use this to prioritize triage and response, as this combination may
indicate host compromise.
"""
from = "now-8h"
interval = "1h"
language = "esql"
license = "Elastic License v2"
name = "Alerts in Different ATT&CK Tactics by Host"
risk_score = 73
rule_id = "29531d20-0e80-41d4-9ec6-d6b58e4a475c"
severity = "high"
tags = ["Use Case: Threat Detection", "Rule Type: Higher-Order Rule", "Resources: Investigation Guide"]
timestamp_override = "event.ingested"
type = "esql"

query = '''
from .alerts-security.*  metadata _id

// filter for medium-or-higher severity alerts, excluding threat_match, machine_learning, and deprecated rules.
| where kibana.alert.risk_score > 21 and
        kibana.alert.rule.name IS NOT NULL and kibana.alert.rule.rule_id IS NOT NULL and
        host.id is not null and event.dataset is not null and
        kibana.alert.rule.type not in ("threat_match", "machine_learning") and
        // Exclude a deprecated rule whose alert name does not carry the standard prefix
        kibana.alert.rule.name != "Potential PrintNightmare File Modification" and
        not kibana.alert.rule.name like "Deprecated - *" and
        not KQL("""kibana.alert.rule.tags : "Rule Type: Higher-Order Rule" """)

// extract unique counts and values by host.id
| stats Esql.alerts_count = COUNT(*),
        Esql.kibana_alert_rule_name_distinct_count = COUNT_DISTINCT(kibana.alert.rule.name),
        Esql.kibana_alert_rule_id_distinct_count = COUNT_DISTINCT(kibana.alert.rule.rule_id),
        Esql.event_module_values = VALUES(event.module),
        Esql.host_name_values = VALUES(host.name),
        Esql.kibana_alert_rule_name_values = VALUES(kibana.alert.rule.name),
        Esql.kibana_alert_rule_id_values = VALUES(kibana.alert.rule.rule_id),
        Esql.threat_tactic_id_distinct_count = COUNT_DISTINCT(kibana.alert.rule.threat.tactic.id),
        Esql.threat_tactic_name_values = VALUES(kibana.alert.rule.threat.tactic.name),
        Esql.process_executable_values = VALUES(process.executable),
        Esql.process_parent_executable_values = VALUES(process.parent.executable),
        Esql.process_command_line_values = VALUES(process.command_line),
        Esql.process_entity_id_distinct_count = COUNT_DISTINCT(process.entity_id) by host.id

// filter for risky hosts with multiple distinct rules across multiple tactics
// Distinct rule IDs prevent one rule mapped to multiple tactics from satisfying the correlation.
| where Esql.kibana_alert_rule_name_distinct_count >= 2 and
        Esql.kibana_alert_rule_id_distinct_count >= 2 and
        Esql.threat_tactic_id_distinct_count >= 3

// Populate the native host name for alert triage without changing the host.id correlation key.
| eval host.name = MV_FIRST(Esql.host_name_values)

// fields populated in the resulting alert
| keep host.id,
       host.name,
       Esql.alerts_count,
       Esql.kibana_alert_rule_name_distinct_count,
       Esql.kibana_alert_rule_id_distinct_count,
       Esql.process_entity_id_distinct_count,
       Esql.event_module_values,
       Esql.host_name_values,
       Esql.kibana_alert_rule_name_values,
       Esql.kibana_alert_rule_id_values,
       Esql.threat_tactic_name_values,
       Esql.process_executable_values,
       Esql.process_parent_executable_values,
       Esql.process_command_line_values
'''
note = """## Triage and analysis

> **Disclaimer**:
> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.

### Investigating Alerts in Different ATT&CK Tactics by Host

The rule identifies hosts with alerts across multiple ATT&CK tactics, which may indicate compromise. It helps analysts focus on high-risk hosts by correlating diverse alerts. The resulting alert is grouped, so the `Esql.*_values` fields summarize contributing alerts without preserving event order or relationships between values.

### Possible investigation steps

- Review the alert details to identify the specific host involved and the different ATT&CK tactics that triggered the alerts.
- Examine the timeline of the alerts to understand the sequence of events and determine if there is a pattern or progression in the tactics used.
- Correlate the alert data with other logs and telemetry from the host, such as process creation, network connections, and file modifications, to gather additional context.
- Investigate any known vulnerabilities or misconfigurations on the host that could have been exploited by the adversary.
- Check for any indicators of compromise (IOCs) associated with the alerts, such as suspicious IP addresses, domains, or file hashes, and search for these across the network.
- Assess the impact and scope of the potential compromise by determining if other hosts or systems have similar alerts or related activity.

### False positive analysis

- Alerts from routine administrative tasks may trigger multiple tactics. Review and exclude known benign activities such as scheduled software updates or system maintenance.
- Security tools running on the host might generate alerts across different tactics. Identify and exclude alerts from trusted security applications to reduce noise.
- Automated scripts or batch processes can mimic adversarial behavior. Analyze and whitelist these processes if they are verified as non-threatening.
- Frequent alerts from development or testing environments can be misleading. Consider excluding these environments from the rule or applying a different risk score.
- User behavior anomalies, such as accessing multiple systems or applications, might trigger alerts. Implement user behavior baselines to differentiate between normal and suspicious activities.

### Response and remediation

- Isolate the affected host from the network immediately to prevent further lateral movement by the adversary.
- Conduct a thorough forensic analysis of the host to identify the specific vulnerabilities exploited and gather evidence of the attack phases involved.
- Remove any identified malicious software or unauthorized access tools from the host, ensuring all persistence mechanisms are eradicated.
- Apply security patches and updates to the host to address any exploited vulnerabilities and prevent similar attacks.
- Restore the host from a known good backup if necessary, ensuring that the backup is free from compromise.
- Monitor the host and network for any signs of re-infection or further suspicious activity, using enhanced logging and alerting based on the identified attack patterns.
- Escalate the incident to the appropriate internal or external cybersecurity teams for further investigation and potential legal action if the attack is part of a larger campaign."""

[rule.investigation_fields]
field_names = [
    "host.name",
    "host.id",
    "Esql.threat_tactic_name_values",
    "Esql.kibana_alert_rule_name_values",
    "Esql.alerts_count",
]

Stages and Predicates

Stage 1: from

from .alerts-security.*  metadata _id

Stage 2: where

| where kibana.alert.risk_score > 21 and
        kibana.alert.rule.name IS NOT NULL and kibana.alert.rule.rule_id IS NOT NULL and
        host.id is not null and event.dataset is not null and
        kibana.alert.rule.type not in ("threat_match", "machine_learning") and
        kibana.alert.rule.name != "Potential PrintNightmare File Modification" and
        not kibana.alert.rule.name like "Deprecated - *" and
        not KQL("""kibana.alert.rule.tags : "Rule Type: Higher-Order Rule" """)

Stage 3: stats

| stats Esql.alerts_count = COUNT(*),
        Esql.kibana_alert_rule_name_distinct_count = COUNT_DISTINCT(kibana.alert.rule.name),
        Esql.kibana_alert_rule_id_distinct_count = COUNT_DISTINCT(kibana.alert.rule.rule_id),
        Esql.event_module_values = VALUES(event.module),
        Esql.host_name_values = VALUES(host.name),
        Esql.kibana_alert_rule_name_values = VALUES(kibana.alert.rule.name),
        Esql.kibana_alert_rule_id_values = VALUES(kibana.alert.rule.rule_id),
        Esql.threat_tactic_id_distinct_count = COUNT_DISTINCT(kibana.alert.rule.threat.tactic.id),
        Esql.threat_tactic_name_values = VALUES(kibana.alert.rule.threat.tactic.name),
        Esql.process_executable_values = VALUES(process.executable),
        Esql.process_parent_executable_values = VALUES(process.parent.executable),
        Esql.process_command_line_values = VALUES(process.command_line),
        Esql.process_entity_id_distinct_count = COUNT_DISTINCT(process.entity_id) by host.id

Stage 4: where

| where Esql.kibana_alert_rule_name_distinct_count >= 2 and
        Esql.kibana_alert_rule_id_distinct_count >= 2 and
        Esql.threat_tactic_id_distinct_count >= 3

Stage 5: eval

| eval host.name = MV_FIRST(Esql.host_name_values)

Stage 6: keep

| keep host.id,
       host.name,
       Esql.alerts_count,
       Esql.kibana_alert_rule_name_distinct_count,
       Esql.kibana_alert_rule_id_distinct_count,
       Esql.process_entity_id_distinct_count,
       Esql.event_module_values,
       Esql.host_name_values,
       Esql.kibana_alert_rule_name_values,
       Esql.kibana_alert_rule_id_values,
       Esql.threat_tactic_name_values,
       Esql.process_executable_values,
       Esql.process_parent_executable_values,
       Esql.process_command_line_values

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
host.idKEEP host.id
host.nameKEEP host.name
Esql.alerts_countKEEP Esql.alerts_count
Esql.kibana_alert_rule_name_distinct_countKEEP Esql.kibana_alert_rule_name_distinct_count
Esql.kibana_alert_rule_id_distinct_countKEEP Esql.kibana_alert_rule_id_distinct_count
Esql.process_entity_id_distinct_countKEEP Esql.process_entity_id_distinct_count
Esql.event_module_valuesKEEP Esql.event_module_values
Esql.host_name_valuesKEEP Esql.host_name_values
Esql.kibana_alert_rule_name_valuesKEEP Esql.kibana_alert_rule_name_values
Esql.kibana_alert_rule_id_valuesKEEP Esql.kibana_alert_rule_id_values
Esql.threat_tactic_name_valuesKEEP Esql.threat_tactic_name_values
Esql.process_executable_valuesKEEP Esql.process_executable_values
Esql.process_parent_executable_valuesKEEP Esql.process_parent_executable_values
Esql.process_command_line_valuesKEEP Esql.process_command_line_values