9.3 Event Correlation Rules with Event Query Language (EQL)

Key Takeaways

  • Event Query Language (EQL) is an event-oriented declarative correlation language purpose-built for threat hunting and detecting multi-stage adversary behaviors across chronological event streams.

  • EQL sequence queries correlate distinct event categories (such as process execution, file creation, registry modification, and network connections) that occur in a specific temporal order within a constrained time span (maxspan).

  • Correlation keys defined via the by keyword bind events across the sequence to shared entity identities (e.g., by host.id, process.entity_id), ensuring that multi-stage actions are correlated to the exact same host, user, or process lineage.

  • The until clause acts as a sequence cancellation constraint, terminating correlation matching if a benign or expected intervening event occurs before subsequent attack steps execute.

  • EQL missing-event clauses (written ![ ... ] and requiring with maxspan) alert when an expected event fails to occur within the sequence window.

Last updated: September 2026

The Necessity of Stateful Correlation in Modern Threat Detection

Single-event detection rules (such as Custom Query and Threshold rules) evaluate documents in isolation. However, sophisticated adversaries rarely execute an entire attack in a single command. Real-world cyber attacks unfold as a progressive sequence of actions across the cyber kill chain: an initial exploit or phishing email triggers process execution, which drops a payload to disk, modifies a persistence registry key, and ultimately establishes an encrypted outbound network beacon to a Command and Control (C2) server.

Evaluating any one of these events in isolation frequently leads to either false positives or false negatives. For example, running powershell.exe is common administrative behavior. Creating a file in C:\Users\Public\ may be benign. Establishing an outbound HTTPS connection is normal web traffic. But when the same PowerShell process drops an executable file and immediately opens an external network socket, the sequence represents a high-confidence attack. Event Query Language (EQL) is Elastic's purpose-built declarative query language designed specifically to correlate chronological event sequences across diverse ECS telemetry categories.


EQL Syntax Fundamentals: Event Types and Conditions

EQL is native to Elasticsearch and operates directly on ECS-compliant documents. Unlike KQL or Lucene, which operate on broad document fields, EQL queries begin by specifying the event category followed by a conditional expression enclosed in a where block:

category where condition
+-------------------------------------------------------------+
| EQL Event Anatomy                                           |
|                                                             |
|   process where event.type == "start" and                   |
|     process.name == "cmd.exe" and                           |
|     process.parent.name == "winword.exe"                    |
|   \_____/       \______________________________________/    |
|      |                             |                        |
|  Event Category              Boolean Conditions             |
+-------------------------------------------------------------+

1. ECS Event Categories

EQL maps directly to the ECS event.category field. Common EQL event categories include:

  • process: Process creation, start, and termination events (event.category: "process").
  • file: File creation, modification, and deletion events (event.category: "file").
  • network: Inbound and outbound network connections, DNS queries, and flow records (event.category: "network").
  • registry: Windows registry key and value modifications (event.category: "registry").
  • authentication: User logons, Kerberos ticket grants, and logoffs (event.category: "authentication").

2. Operators and Functions

EQL supports rich comparison operators and string evaluation functions:

  • Comparison Operators: equality (==), inequality (!=), and relational numeric comparisons (less than, less than or equal to, greater than, greater than or equal to).
  • Logical Operators: and, or, not
  • Set Membership: process.name in ("powershell.exe", "pwsh.exe", "cmd.exe")
  • Wildcard and Case-Insensitive Matching:
    • == is an exact, case-sensitive comparison; : is a case-insensitive comparison that also accepts * and ? wildcards: process.name : "POWERSHELL.EXE"
    • like matches wildcards case-sensitively and like~ case-insensitively: process.command_line like~ "*downloadstring*"
    • in checks a list case-sensitively and in~ case-insensitively: process.name in~ ("powershell.exe", "pwsh.exe")
  • Strings: Always use double quotes. EQL does not accept single-quoted strings.
  • String Functions: concat(), stringContains(), length(), startsWith(), endsWith()

Sequence Queries and Temporal Constraints: sequence with maxspan

The true power of EQL lies in sequence queries. A sequence specifies two or more chronological steps that must occur in an exact temporal order.

sequence with maxspan=5m
  [process where event.type == "start" and process.name == "mshta.exe"]
  [network where event.type == "start" and destination.port in (80, 443)]

1. The maxspan Parameter

The maxspan clause defines the maximum allowable time window between the first matching event and the final matching event in the sequence:

  • Format: maxspan=5m (5 minutes), maxspan=1h (1 hour), maxspan=30s (30 seconds).
  • If Step 1 occurs at 10:00:00 and Step 2 occurs at 10:06:00 with maxspan=5m, no alert is generated because the elapsed time (6 minutes) exceeds the constraint.

2. Strict Chronological Ordering

EQL enforces temporal ordering based on @timestamp, with an optional tiebreaker field (such as event.sequence) set in the rule's EQL settings for events with identical timestamps. In the sequence above, the network event must occur after the process start event. If the network event occurred at 10:00:00 and mshta.exe started at 10:01:00, the sequence does not match.


Correlation Keys: The by Keyword and Entity Binding

In an enterprise generating millions of events across thousands of endpoints, simply verifying that Step 1 and Step 2 occurred within 5 minutes across the organization is insufficient. An mshta.exe process executing on Workstation A and a network connection occurring on Server B within 5 minutes are completely unrelated.

EQL solves this using correlation keys defined via the by keyword. Correlation keys bind sequence steps to shared entity identifiers:

sequence with maxspan=5m
  [process where event.type == "start" and process.name == "mshta.exe"] by host.id, process.entity_id
  [network where event.type == "start" and destination.port in (80, 443)] by host.id, process.entity_id
Timeline:
  T0: Host A (PID 1024 / mshta.exe) Starts
       | [Correlation Key: host.id = A, process.entity_id = 1024]
       v
  T1: Host B (PID 9988 / curl.exe) Connects Outbound
       | [Correlation Key: host.id = B, process.entity_id = 9988] -> NO MATCH (Wrong Host/PID)
       v
  T2: Host A (PID 2048 / svchost.exe) Connects Outbound
       | [Correlation Key: host.id = A, process.entity_id = 2048] -> NO MATCH (Wrong PID)
       v
  T3: Host A (PID 1024 / mshta.exe) Connects Outbound to Port 443
       | [Correlation Key: host.id = A, process.entity_id = 1024] -> EXACT MATCH!
       v
  [ ALERT SIGNAL GENERATED: Full Attack Path Correlated ]

Why process.entity_id is Mandatory

In Windows and Linux operating systems, OS Process IDs (PIDs) are recycled over time. Furthermore, two different machines frequently run processes with the exact same numerical PID. Elastic Defend generates a globally unique identifier for every running process stored in process.entity_id.

  • Binding by by host.id, process.entity_id guarantees that the network connection was initiated by the exact same running process instance that spawned mshta.exe on that specific host.

Cross-Field Relationship Binding

Correlation keys are not restricted to identical field names. EQL allows binding parent and child relationships across sequence steps:

sequence with maxspan=1m
  [process where event.type == "start" and process.name == "cmd.exe"] by host.id, process.entity_id
  [process where event.type == "start" and process.name == "whoami.exe"] by host.id, process.parent.entity_id

Here, the sequence binds the process.entity_id of Step 1 to the process.parent.entity_id of Step 2, mathematically verifying that cmd.exe directly spawned whoami.exe.


Real-World Threat Scenarios with EQL

Scenario 1: Living off the Land Binaries (LOLBins) & In-Memory Execution

Adversaries frequently use trusted system binaries to download and execute malicious code, bypassing traditional application allow-listing.

sequence with maxspan=5m
  [process where event.type == "start" and 
   process.name in~ ("powershell.exe", "pwsh.exe", "cmd.exe") and
   process.args in~ ("-enc", "-encodedcommand", "-e")] by host.id, process.entity_id
  [file where event.action in ("creation", "modification") and 
   file.extension in ("exe", "dll", "vbs", "ps1")] by host.id, process.entity_id
  [network where event.type == "start" and 
   not cidrmatch(destination.ip, "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")] by host.id, process.entity_id
  • Analysis: Detects an encoded PowerShell or command script that drops an executable or script to disk and establishes an outbound non-RFC1918 internet connection, all within 5 minutes and executed by the same process.

Scenario 2: LSASS Memory Dumping for Credential Theft

Adversaries target the Local Security Authority Subsystem Service (lsass.exe) to extract plaintext passwords, NTLM hashes, and Kerberos tickets.

sequence with maxspan=3m
  [process where event.type == "start" and 
   process.name in~ ("rundll32.exe", "procdump.exe", "procdump64.exe")] by host.id, process.entity_id
  [file where event.action in ("creation", "modification") and 
   file.extension in ("dmp", "dump") and 
   file.path : ("*\\AppData\\Local\\Temp\\*", "*\\Users\\Public\\*")] by host.id, process.entity_id
  • Analysis: Correlates the execution of common memory dumping utilities (rundll32 calling comsvcs.dll or Sysinternals procdump) with the creation of a .dmp file in temporary directories.

Scenario 3: Persistence Mechanism Creation via Scheduled Tasks

Adversaries establish persistence by scheduling recurring tasks after staging malicious scripts.

sequence with maxspan=10m
  [file where event.action == "creation" and 
   file.path : "C:\\Users\\*\\AppData\\Roaming\\*.bat"] by host.id
  [process where event.type == "start" and 
   process.name == "schtasks.exe" and 
   process.args : "/create"] by host.id
  • Analysis: Correlates the staging of a batch file in user AppData with an immediate schtasks /create invocation on the same host.

Advanced Sequence Modifiers: The until Cancellation Clause

In complex operational environments, a sequence of events might look suspicious only if an expected authorizing or terminating event fails to occur. The until keyword defines a cancellation condition that terminates sequence matching.

sequence with maxspan=15m
  [process where event.type == "start" and process.name == "vpn_client.exe"] by host.id, user.name
  [network where event.type == "start" and destination.port == 22] by host.id, user.name
until
  [authentication where event.action == "mfa_success"] by host.id, user.name

How until Operates:

  • If vpn_client.exe starts, the Detection Engine enters a pending sequence state for that (host.id, user.name) tuple.
  • If an mfa_success authentication event occurs before the network connection to port 22, the pending sequence state is immediately discarded and reset.
  • If the network connection occurs without any intervening MFA validation within 15 minutes, the sequence completes and generates an alert.
  • Operational Benefit: Drastically reduces false positives by factoring in expected administrative validation steps.

EQL Missing Events: Detecting Non-Occurrences

Traditional detection rules identify when a bad event happens. EQL missing event clauses detect when an expected event does not happen inside a time-bounded sequence. Prefix a clause with !:

sequence by host.name, user.name with maxspan=5s
  [ authentication where event.code : "4624" ]
  ![ authentication where event.code : "4647" ]

This example from Elastic's EQL reference finds logons that are not followed by a logoff within 5 seconds. The rules are:

  • with maxspan is mandatory whenever a sequence contains a missing-event clause.
  • Missing-event clauses can appear at the beginning, in the middle, or at the end of a sequence, and a sequence can have several, but it needs at least one positive clause.
  • Event correlation rules support this syntax, so you can alert on absences directly.

SecOps Use Cases for Missing-Event Sequences:

  1. Endpoint Tampering: A security service stops and is not restarted within a few minutes:
    sequence by host.id with maxspan=5m
      [process where event.type == "end" and process.name : "elastic-agent.exe"]
      ![process where event.type == "start" and process.name : "elastic-agent.exe"]
    
  2. Backup Pipeline Failure: A snapshot job starts, but no completion event follows within the expected window.

Missing-event sequences give proactive alerting on stealthy defense evasion, such as adversaries terminating logging daemons or suppressing reporting agents.


EQL Syntax and Construction Reference

The following reference table summarizes the essential syntax elements and keywords of Event Query Language in Elastic Security:

EQL ConstructSyntax ExampleBehavioral FunctionSecOps Detection Application
Event Queryprocess where event.type == "start"Evaluates single-event categoriesBaseline signature detection for malicious utilities.
sequencesequence with maxspan=5m [step1] [step2]Chronological multi-event correlationMulti-stage attack chain tracking (e.g. drop + execute).
maxspanwith maxspan=10mDefines maximum duration between stepsRestricts correlation window to realistic attack speeds.
byby host.id, process.entity_idBinds steps to shared entity keysEliminates cross-host and cross-process false matches.
untiluntil [event where condition]Cancels pending sequence matchSuppresses alerts if authorized or remediating actions occur.
inprocess.name in ("cmd.exe", "pwsh.exe")Set membership matchingEfficiently matches multiple authorized or suspicious binaries.
: / like~ / in~file.path : "*\\Temp\\*"Case-insensitive match; : and like~ accept wildcardsFlexible path matching across heterogeneous OS environments.
![ ]![process where event.type == "start"]Missing event (requires with maxspan)Alerts when an expected event never arrives.
cidrmatchnot cidrmatch(destination.ip, "10.0.0.0/8")Subnet membership evaluationDistinguishes internal lateral movement from external C2.
Loading diagram...
EQL Multi-Stage Attack Sequence Correlation with Entity Binding
Test Your Knowledge

A security analyst deploys the following EQL correlation rule in Elastic Security:

sequence with maxspan=5m [process where event.type == "start" and process.name == "mshta.exe"] by host.id, process.entity_id [network where event.type == "start" and destination.port in (80, 443)] by host.id, process.entity_id

During testing, mshta.exe launches on Host A (host.id: '001', process.entity_id: 'PID-9821'). Two minutes later, an outbound HTTPS connection occurs on Host A initiated by svchost.exe (process.entity_id: 'PID-1044'). Why does this EQL sequence query correctly NOT generate an alert?

A

The EQL engine cannot correlate events across different event categories such as process and network.

B

The 2-minute time difference exceeded the rule's maxspan parameter.

C

The network event query failed because destination ports must be defined as string literals rather than integers.

D

The sequence requires both steps to share identical correlation keys; while the host ID matched, the process entity IDs differed, preventing false cross-process correlation.

Test Your Knowledge

When developing an EQL sequence rule to detect unauthorized administrative privilege escalation, an engineer wants the sequence correlation to terminate and reset if an authorized administrator approves a secondary authentication prompt (event.action == "mfa_success") within the correlation window. Which EQL construct implements this behavior?

A

The 'drop' directive placed immediately after the maxspan declaration.

B

The 'until' clause specifying the cancellation condition and matching entity correlation keys.

C

A negative Lucene filter pill hardcoded into the global dashboard state.

D

The 'fork' keyword configured with an automated timeout penalty.

Test Your Knowledge

A SIEM analyst needs to author an EQL sequence rule to detect a Living off the Land (LOLBin) attack: rundll32.exe spawns on a Windows endpoint, and within 3 minutes on that same endpoint, a new dynamic link library (.dll) file is created in C:\Windows\Temp\. Which EQL query correctly expresses this detection logic?

A

process where process.name == "rundll32.exe" and file.path == "C:\\Windows\\Temp\\*"

B

threshold process.name == "rundll32.exe" by host.id count >= 1 within 3m

C

sequence with maxspan=3m [process where event.type == "start" and process.name == "rundll32.exe"] by host.id [file where event.action in ("creation", "modification") and file.path : "C:\\Windows\\Temp\\*"] by host.id

D

sequence with minspan=3m [file where event.type == "start"] by host.name [process where process.name == "rundll32.exe"] by host.name

Sections you finish are checked off in the contents.