ELQ query language
ELQ (ELLIO Query) is the query language behind ELLIO Threat Intelligence
search. It powers the dashboard search bar and the CTI search API
(/v1/cti/search, /v1/cti/search-docs, /v1/cti/search-aggs) through the
q parameter. Queries are field: value clauses combined with boolean
operators, lists, ranges, and wildcards:
classification: malicious AND network.port IN (22, 2222) AND last_seen: 7dBare indicators
Section titled “Bare indicators”Paste an indicator on its own - ELQ recognises the shape and searches the right field automatically:
| You type | Interpreted as |
|---|---|
198.51.100.7 |
ip: "198.51.100.7" |
203.0.113.0/24 |
ip: "203.0.113.0/24" |
CVE-2024-3400 |
cve: "CVE-2024-3400" |
TA0043 |
mitre_attack.tactics: TA0043 |
T1595 |
mitre_attack.techniques: T1595 |
T1595.002 |
mitre_attack.sub_techniques: T1595.002 |
AS13335 |
src.asn.number: 13335 |
t13i190800_9dc949149365_97f8aa674fd9 |
fingerprints.ja4: ... |
64240_2-4-8-1-3_1460_7 |
fingerprints.ja4t: ... |
c36s36_c51s80_c0s0 |
fingerprints.ja4ssh: ... |
| 32-character hex hash | fingerprints.ja3: ... |
Any other bare word runs as free text across the text fields: rdns,
src.asn.name, http.path, http.user_agent,
ssh.auth.username, ssh.auth.password, tag, and actor.
Operators
Section titled “Operators”| Operator | Meaning | Example |
|---|---|---|
: |
term match (any listed value matches on multi-valued fields) | classification: malicious |
!: |
negated term match | classification!: benign |
~: |
full-text match on analysed text fields | http.user_agent ~: zgrab |
!~: |
negated full-text match | http.user_agent !~: curl |
= / != |
exact equality / inequality | network.port = 23 |
> >= < <= |
numeric and date comparison | active_days > 30 |
IN (a, b, c) |
any of the listed values | port IN (22, 23, 2323) |
[a TO b] |
inclusive range (both ends required) | first_seen:[2026-06-01 TO 2026-06-30] |
exists / not_exists |
field presence | rdns not_exists |
AND |
both sides must match (two clauses side by side are also ANDed) | tag: "Shodan" AND spoofable: false |
OR |
either side must match | country: CN OR country: RU |
NOT |
negate a clause or group | NOT tag: "Censys" |
( ) |
group expressions | (country: CN OR country: RU) AND spoofable: false |
* / ? |
wildcard: any run / any single character | rdns: "*.censys-scanner.com" |
"..." |
exact string with spaces or special characters | tag: "Fast Scanner (i.e. Masscan / ZMap)" |
Quote string values that contain spaces, slashes, or special characters.
Numbers, booleans, enum values, and identifiers like T1595 can stay bare.
Relative times
Section titled “Relative times”Date fields accept relative values: last_seen: 7d means “within the last
7 days”. Units are d, w, m, and y; the dashboard suggests 1d,
3d, 7d, 30d, and 90d (document freshness is daily, so 1d is the
smallest useful window). Absolute dates (last_seen >= 2026-07-01) and
ranges (last_seen:[7d TO 1d]) work too.
Aliases
Section titled “Aliases”Common fields have short aliases that resolve silently: port, asn,
ua, path, username, password, country, city, tactic,
technique, sub_technique, and the fingerprint names (muonfp, ja4,
ja4t, ja4h, ja4ssh, ja3). country and src.geo.country match
either the country code or the country name.
Field reference
Section titled “Field reference”47 fields, organised the way the dashboard organises them.
Core (6)
Section titled “Core (6)”| Field | Description | Example |
|---|---|---|
ip |
The IP being searched; accepts single IPs and CIDR blocks | ip: "198.51.100.7", ip: "203.0.113.0/24" |
classification |
ELLIO verdict for the IP | malicious, promiscuous, benign, unknown |
seen |
Whether the IP was observed in the current window | seen: true |
spoofable |
True when the IP never completed a TCP handshake | spoofable: false |
rdns |
Reverse DNS hostname | rdns: "*.censys-scanner.com" |
active_days |
Days the IP was active in the 91-day window | active_days > 30 |
Temporal (2)
Section titled “Temporal (2)”| Field | Description | Example |
|---|---|---|
last_seen |
Most recent observation | last_seen: 1d, last_seen >= 2026-07-01 |
first_seen |
First observation | first_seen: 7d, first_seen:[2026-06-01 TO 2026-06-30] |
Network (5)
Section titled “Network (5)”| Field | Description | Example |
|---|---|---|
network.port |
Destination port the IP touched (any handshake state) | network.port: 23, port IN (22, 23, 2323) |
network.non_spoofable_port |
Port with a completed TCP handshake | network.non_spoofable_port: 445 |
network.spoofable_port |
Port seen only without a completed handshake | network.spoofable_port: 53 |
src.asn.number |
Autonomous system number announcing the IP | src.asn.number: 13335, AS13335 |
src.asn.name |
Name of the announcing autonomous system | src.asn.name: "*cloudflare*" |
Geolocation (14)
Section titled “Geolocation (14)”Each field exists in a src.geo.* variant (where the IP is) and a
dst.geo.* variant (where the ELLIO sensors it targeted are).
| Field | Description | Example |
|---|---|---|
src.geo.country.code / dst.geo.country.code |
ISO 3166-1 alpha-2 country code | country IN (CN, RU) |
src.geo.country.name / dst.geo.country.name |
Country name | src.geo.country.name: "United States" |
src.geo.city / dst.geo.city |
City | city: "Amsterdam" |
src.geo.continent.code / dst.geo.continent.code |
Continent code | src.geo.continent.code: EU |
src.geo.continent.name / dst.geo.continent.name |
Continent name | dst.geo.continent.name: Europe |
src.geo.latitude / dst.geo.latitude |
Latitude | src.geo.latitude > 50 |
src.geo.longitude / dst.geo.longitude |
Longitude | src.geo.longitude < 0 |
HTTP (2)
Section titled “HTTP (2)”| Field | Description | Example |
|---|---|---|
http.path |
Request paths observed from the IP | http.path: "/.env", path: "/wp-*" |
http.user_agent |
User-Agent strings observed from the IP | ua: "*zgrab*" |
SSH (5)
Section titled “SSH (5)”| Field | Description | Example |
|---|---|---|
ssh.auth.username |
Usernames attempted | username: "root" |
ssh.auth.password |
Passwords attempted | password: "admin123" |
ssh.auth.attempt_count |
Attempts for this credential pair | ssh.auth.attempt_count > 100 |
ssh.auth.first_seen |
First observation of the pair | ssh.auth.first_seen: 7d |
ssh.auth.last_seen |
Latest observation of the pair | ssh.auth.last_seen: 1d |
Fingerprints (6)
Section titled “Fingerprints (6)”| Field | Description | Example |
|---|---|---|
fingerprints.muonfp |
MuonFP TCP fingerprint (window_size:tcp_options:mss:window_scale) |
muonfp: "64240:2-4-8-1-3:1460:7", muonfp: "*:::" |
fingerprints.ja4 |
JA4 TLS client fingerprint | ja4: "t13i190800_9dc949149365_97f8aa674fd9" |
fingerprints.ja4t |
JA4T TCP fingerprint | ja4t: "64240_2-4-8-1-3_1460_7" |
fingerprints.ja4h |
JA4H HTTP client fingerprint | ja4h: "ge11nn15enus_42d7418375b7_000000000000_000000000000" |
fingerprints.ja4ssh |
JA4SSH fingerprint | ja4ssh: "c36s36_c51s80_c0s0" |
fingerprints.ja3 |
JA3 TLS client fingerprint (MD5); prefer JA4 where available | ja3: "6734f37431670b3ab4292b8f60f29984" |
Searching by MuonFP
Section titled “Searching by MuonFP”MuonFP fingerprints the TCP SYN packet passively. A value has four
colon-separated segments - window_size:tcp_options:mss:window_scale:
| Segment | Meaning |
|---|---|
window_size |
TCP window advertised in the SYN |
tcp_options |
Option kinds in wire order, joined with dashes (2 = MSS, 4 = SACK permitted, 8 = timestamps, 1 = NOP, 3 = window scale) |
mss |
Maximum segment size, when the MSS option is present |
window_scale |
Window scale value, when that option is present |
So 64240:2-4-8-1-3:1460:7 is a full OS TCP stack: window 64240, the
common five-option sequence, MSS 1460, window scale 7. Segments the SYN
does not populate stay empty but keep their colons - a SYN carrying no TCP
options at all produces values like 1024::: or 65535:::, the hallmark
of raw-socket scanners such as Masscan and ZMap that craft packets without
the kernel’s TCP stack.
Common query shapes:
muonfp: "64240:2-4-8-1-3:1460:7" exact fingerprintmuonfp: "*:::" any optionless SYN (raw-socket tools)muonfp: "1024:::" Masscan-style: window 1024, no optionsmuonfp: "65535:*" window 65535, any optionsmuonfp: "64240:2-4-8-1-3:*:7" same stack, any MSSmuonfp: "??:::" two-digit window, no optionsfingerprints.muonfp exists a SYN fingerprint was recorded at allTwo things to know:
- Bare MuonFP values are not auto-recognised. Unlike JA4 or JA3, a
MuonFP value pasted on its own is not sniffed as an indicator (its colons
read as field syntax), so always spell the field:
muonfp: "...". Quoting is recommended, though unquoted values also parse -muonfp:64240:2-4-8-1-3:1460:7stays in one piece. - MuonFP and JA4T encode the same four SYN attributes. The stack above
appears as
64240:2-4-8-1-3:1460:7in MuonFP and64240_2-4-8-1-3_1460_7in JA4T, so you can pivot between the two fields by swapping separators.
Intelligence (7)
Section titled “Intelligence (7)”| Field | Description | Example |
|---|---|---|
tag |
Human-readable behaviour tag from the detection pipeline | tag: "Telnet Bruteforce" |
tag_id |
Stable identifier of the tag | tag_id: "telnet_bruteforce" |
actor |
Known actor or scanner attribution | actor: "Censys" |
cve |
CVE identifiers tied to exploitation behaviour | cve: "CVE-2024-3400", cve: "CVE-2024-*" |
mitre_attack.tactics |
MITRE ATT&CK tactic IDs | tactic: TA0043 |
mitre_attack.techniques |
MITRE ATT&CK technique IDs | technique: T1595 |
mitre_attack.sub_techniques |
MITRE ATT&CK sub-technique IDs | sub_technique: T1595.002 |
Recipes
Section titled “Recipes”Useful combinations to copy-paste into the search bar.
Mass scanners from China or Russia
Section titled “Mass scanners from China or Russia”country IN (CN, RU) AND tag: "Fast Scanner (i.e. Masscan / ZMap)"High-confidence indicators only
Section titled “High-confidence indicators only”Always filter spoofable sources out of automated decision-making:
classification: malicious AND spoofable: falseFresh exploitation of a specific CVE
Section titled “Fresh exploitation of a specific CVE”cve: "CVE-2024-3400" AND last_seen: 7dNew infrastructure exploiting anything
Section titled “New infrastructure exploiting anything”IPs first seen this week that already carry a CVE association:
first_seen: 7d AND cve exists AND spoofable: falseBrute-forcers hammering SSH
Section titled “Brute-forcers hammering SSH”ssh.auth.username: "root" AND ssh.auth.attempt_count > 100Persistent scanners
Section titled “Persistent scanners”Long-lived promiscuous scanners active most of the window:
classification: promiscuous AND active_days > 60Filter by reverse DNS pattern
Section titled “Filter by reverse DNS pattern”rdns: "*.censys-scanner.com"Credential-file hunters
Section titled “Credential-file hunters”http.path: "/.env" OR http.path: "/.git/config"Pin down a tool by fingerprint
Section titled “Pin down a tool by fingerprint”ja4: "t13i190800_9dc949149365_97f8aa674fd9" AND classification: maliciousRaw-socket mass scanners
Section titled “Raw-socket mass scanners”SYNs with every option segment empty come from tools that build packets in userspace instead of using the OS TCP stack - Masscan, ZMap, and friends:
muonfp: "*:::" AND last_seen: 7dNarrow it to Masscan’s signature window size:
muonfp: "1024:::"Same TCP stack, new infrastructure
Section titled “Same TCP stack, new infrastructure”Scan fleets rotate IPs and ASNs far more often than they rebuild their TCP stack. Take the MuonFP value from an IP you already caught and look for fresh arrivals wearing the same stack:
muonfp: "64240:2-4-8-1-3:1460:7" AND first_seen: 7d AND spoofable: falseCross-region reconnaissance
Section titled “Cross-region reconnaissance”IPs in one continent targeting ELLIO sensors in another:
src.geo.continent.code: AS AND dst.geo.continent.code: NA- Paste indicators directly. IPs, CIDRs, CVEs, ATT&CK IDs, ASNs, and JA3/JA4-family fingerprints work without a field name. MuonFP is the exception - its colons read as field syntax, so give it the field name:
muonfp: "...". - Quote anything with spaces or special characters. Tag names, organisations, RDNS hostnames, HTTP paths, and SSH credentials almost always need quotes.
- Use wildcards to match families.
rdns: "*.censys-scanner.com"catches every Censys scanner host;tag: "*Scanner*"catches every scanner tag. - Group with parentheses before joining with
AND. MixingORandANDwithout parentheses leads to surprising precedence. - Add
spoofable: falseto anything you feed into a blocklist or alerting pipeline - spoofable observations can come from a forged source. - Let the search bar help. Autocomplete suggests fields, operators, and live values from the current index as you type, and flags syntax problems with one-click fixes before the search runs.
What’s next
Section titled “What’s next”- IP detail page - every field in this reference, rendered for a single IP.
- Tags - the canonical list of values for the
tagfield. - CTI API - run the same ELQ queries programmatically via the
qparameter of the search endpoints, alongside per-IP lookup.