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.