Reflex Query Language (RQL)
Reflex Query Language (RQL) is a query language designed to query events within Case-Hub. RQL is used while creating Event Rules to query the Event data and match certain criteria.
Event Fields
observables|observables.*valuetlptagsspottedsafesource_fielddata_typeiocoriginal_source_fieldtitledescriptiontlpseveritystatus,referencesourcesignaturetagsraw_log|raw_log.*
Supported Expressions
The following Expressions are used to compare target field data to intended data:
RegExp
The item has a value that matches a regular expression
title RegExp "^Event.*"
In
The item has a value in a list of values
observables.tags.name in ["malware"]
Contains
The target value contains a specific string
description Contains "malware"
ContainsCIS
Same as Contains but will make the target value and checked value the same case
=|eq|Eq
Equal to (strings or numbers)
!=|ne|Ne|NE
Not equal to (strings or numbers)
>|gt
Greater than (> or gt)
>=|gte
Greater than or equal to (>= or gte)
<|lt
Less than (< or lt)
<=|lte
Less than or equal to (<= or lte)
InCIDR
The IP address is in a specific CIDR network
192.168.1.1 InCIDR 192.168.0.0/16
Is
Use for boolean operations like "Is True"
ioc Is True
Exists
The field exists at all, useful if you don't care what the target value
Between
The item has a value between a given range
tlp Between "1,3"
StartsWith
The item has a value that starts with a certain string
url StartsWith "https"
EndsWith
The item has a value that ends with a certain string
domain EndsWith ".tk"
Expand
When you want two conditions on a nested object to be true use each statement.
For example, The observable 127.0.0.1 should have a value of 127.0.0.1 and safe is True, the query would look like Expand observables (value = "127.0.0.1" and safe Is True)
Intel|Threat
Allows RQL to check the value of a field against a Case-Hub Intel List (threat lists)
Not
The expression within this block should not match.
Note: Can only be used on eq, in, InCIDR, contains, between AND regexp.
title Not Eq "Suspicious DNS Query"
Note that the Items that don't have a specified field may match a Not expression e.g. ip NOT InCIDR "192.168.0.0/16" may match on an event not having an ip field, pair this with ip exists AND ip NOT InCIDR "192.168.0.0/16"
Mutators
Mutators take a field and perform an extra operation on it to make it digestible by the downstream comparison. Assume you want to find any Event with a domain observable where the length of the domain is longer than 20 characters
|length- How long a string is e.g.url.domain|length > 20|count- The number of items in an array valueobservables|count > 2|lowercase- Lowercase a string (redundant forContainsCISbut can be used for other fields)|refang- If for some reason the target value has been defanged, thinkhXXps[:]//instead ofhttps://this will refang the value to perform a proper comparison e.g.url.full|refang eq "https://www.google.com"|b64decode- If the target value is base64 encoded and you want to write rules using the terms within it, first base64 decode it, e.g.command|b64decode Contains "Invoke-Mimikatz"|b64extract- Will attempt to find base64 encoded data in a string and extract it for comparison later in the query e.g. an event contains a commandpowershell -encodedCommand SQBuAHYAbwBrAGUALQBXAGUAYgBSAGUAcQB1AGUAcwB0ACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AaQBwAGkAbgBmAG8ALgBpAG8ALwBpAHAAwould extract and decodeSQBuAHYAbwBrAGUALQBXAGUAYgBSAGUAcQB1AGUAcwB0ACAAaAB0AHQAcABzADoALwAvAHcAdwB3AC4AaQBwAGkAbgBmAG8ALgBpAG8ALwBpAHAAtoInvoke-WebRequest https://www.ipinfo.io/ip. An analyst could then use a query likecommand|b64extract Contains "ipinfo.io"|urldecode- Unescapes an escaped URL so that direct comparisons can be made|any- Force the following condition to match any item in the array (Can only be used onContainsandInexpressions)|all- Force the condition to match all items in the array (Can only be used onContainsandInexpressions)|avg- Calculates the average value given a list of values e.g.observables.risk_score|avg > 7|max- Finds the max value given a list of values e.g.vulnerablities.cvss_score|max > 7|min- Finds the minimum value given a list of values|sum- Add up all the values in a list of integer or float values|split- Splits a string that contains spaces into an array|geo_country- Returns the ISO Code for the country an IP resides in|geo_continent- Returns the ISO Code for the content an IP resides in|geo_timezone- Returns the timezone an IP resides in|reverse_lookup- Takes an IP and attempts to return the associated hostname|nslookup_a- Fetches the A record for a domain name|nslookup_aaaa- Fetches the AAAA record for a domain name|nslookup_mx- Fetches the MX records for a domain name|nslookup_ns- Fetches the NS records for a domain name|nslookup_ptr- Fetches the PTR records for a domain|is_private- Returns True if an IP is RFC1918|is_global- Returns True if an IP is routable on the internet|is_multicast- Returns True if an IP is multicast|is_ipv6- Returns True if an IP is IPv6
Query Examples
# Match any Suspicious DNS query only if it came from the Administrator on a domain joined machine and the target observable is evil.com
title = "Suspicious DNS Query" and user Contains "Administrator" and hostname EndsWith "ad.blusapphire.com" and (observables.data_type = "domain" and observables.value = "evil.com")# Match any cases referencing malware or with a severity higher than 3
description Contains "malware" or severity > 3# Match any event that has a domain with evil.com or an IP with 127.0.0.1
(observables.data_type = "domain" and observables.value = "evil.com") or (observables.data_type = "ip" and observables.value = "127.0.0.1")# Match any Event that has all of the below observables
observables.values|all in ["evil.com","blusapphire.com"]# Match any event that contains a base64 encoded command that once decoded contains the following phrases
command exists and command|b64decode|lowercase Contains ["invoke-mimikatz","invoke-bloodhound","invoke-powerdump","invoke-kerberoast"]# Match any suspicious DNS query for a specific domain that originates from a specific user and source process
# Should always be this event title
title = "Suspicious DNS Query"
# Observables should always exist
and observables exists
# Brian must be the source user of the event
and expand observables (value Contains "Brian" and source_field|lowercase = "winlog.event_data.user")
# And the DNS resolution should always be for netwars-support.slack.com
and expand observables (value = "netwars-support.slack.com" and source_field|lowercase = "dns.question.name")
# And should originate from Slack
and raw_log.process.executable EndsWith "slack.exe"# Check to see if the user in the event is in the Allowed Users intel list
expand observables ( data_type = "user" and intel(value|uppercase, 'Allowed Users'))Last updated