Lesson 4 of 5 · 25 min read

EQL & ES|QL — Sequences, Event Correlation, Pipe Queries

EQL (Event Query Language) and ES|QL are Elastic’s two query languages. EQL’s sequence detection is the most powerful native construct for expressing multi-step attack chains — it’s what makes Elastic Security compelling for complex threat detection.


EQL: Built for Attack Chains

EQL’s core abstraction is the sequence — an ordered series of events that must occur on the same entity (same host, same user, same process) within a time window.

Single-event EQL query

// Find all process creation events for cmd.exe
process where process.name == "cmd.exe"
  and process.parent.name == "explorer.exe"

EQL event categories: process, network, file, registry, authentication, library, driver, any.

Sequence detection

// Detect web shell execution: web server spawning a shell, then making a network connection
sequence by host.name with maxspan=5m
  [process where
    process.parent.name in ("w3wp.exe", "httpd", "nginx", "tomcat")
    and process.name in ("cmd.exe", "powershell.exe", "bash", "sh")]
  [network where
    network.direction == "egress"
    and destination.port in (4444, 1337, 8080, 443)]

This fires only if BOTH events occur on the SAME host within 5 minutes, in that order. A web process spawning a shell that then makes an outbound connection.

Three-event chain: discovery → lateral movement

sequence by user.name with maxspan=10m
  // Step 1: Reconnaissance
  [process where process.name in ("net.exe", "whoami.exe", "nltest.exe", "dsquery.exe")]
  // Step 2: Credential access
  [process where
    process.name in ("procdump.exe", "mimikatz.exe", "lsadump.exe")
    or (process.name == "powershell.exe" and process.command_line like~ "*lsass*")]
  // Step 3: Lateral movement
  [network where
    destination.port in (445, 135, 5985)  // SMB, RPC, WinRM
    and network.direction == "egress"]

This catches the post-initial-access pattern: recon → credential dump → lateral movement, all by the same user in 10 minutes.


EQL Modifiers and Operators

Case-insensitive matching

// like~ is case-insensitive wildcard
process where process.name like~ "powershell*"

// == is case-sensitive
process where process.name == "PowerShell.exe"  // would miss "powershell.exe"

Negation and arrays

process where
  process.parent.name in ("cmd.exe", "powershell.exe")
  and process.name not in ("conhost.exe", "WerFault.exe")
  and not process.command_line like~ "*\\Program Files\\*"

until operator: stop the sequence

// Process injection: CreateRemoteThread within 60s of LSASS access, but not if terminated before
sequence by process.entity_id with maxspan=60s
  [process where process.name == "powershell.exe"]
  [process where event.action == "CreateRemoteThread" and process.parent.name == "powershell.exe"]
  until [process where event.action == "end" and process.name == "powershell.exe"]

until stops tracking the sequence as soon as the until event fires — avoids false matches from long-lived processes.


ES|QL: Modern Analytics Language

ES|QL is Elastic’s newer general-purpose analytics language, designed to replace EQL for non-sequence use cases. It uses the same pipe model as KQL:

FROM logs-*
| WHERE event.category == "process" AND process.name == "powershell.exe"
| WHERE @timestamp > NOW() - 24 HOURS
| STATS count=COUNT(*), hosts=COUNT_DISTINCT(host.name) BY process.parent.name
| WHERE count > 10
| SORT count DESC
| LIMIT 50

ES|QL for suspicious parent process analysis

FROM .ds-logs-endpoint.events.process-*
| WHERE event.action == "start"
| WHERE process.parent.name IN ("word.exe", "excel.exe", "outlook.exe")
| WHERE process.name IN ("cmd.exe", "powershell.exe", "wscript.exe", "mshta.exe")
| KEEP @timestamp, host.name, user.name, process.parent.name, process.name, process.command_line
| SORT @timestamp DESC

ES|QL KEEP is equivalent to KQL’s project — selects output columns. ES|QL uses ALL_CAPS keywords; field references are lowercase with dots.

EQL Reference— Elastic Docs ES|QL Reference— Elastic Docs