uAQL - uberAgent Query Language
Overview
The uberAgent Query Language (uAQL) is the language for uberAgent’s powerful Threat Detection and Event Data Filtering queries. Take a look at this blog post for an quick introduction.
uAQL query strings evaluate to either true
or false
. A simple example: the query Process.Name == "explorer.exe"
tests if the current process name is equal to explorer.exe
.
- If yes, the query yields
true
- If no, the query yields
false
Data Types
uAQL is a type-safe language supporting the following data types:
- Integer (a number)
- Boolean (
true
orfalse
) - String (a UTF-8 string literal, in other words: text)
Integer
A uAQL integer is a 64-bit signed number. Valid values are in the range between -9223372036854775808
and 9223372036854775807
.
Boolean
A uAQL boolean is either true
or false
. Strings are implicitly converted to false
. Integers with the value 1
are implicitly converted to true
. All other integers are implicitly converted to false
.
String
A uAQL string represents a sequence of characters that are wrapped in double quotes ("
) or single quotes ('
), e.g., "string 1"
or 'string 2'
. Mixing different types of quotes (e.g., "uberAgent'
) is not allowed and results in a syntax error.
Character Escaping
Special characters can be specified by escaping them with a prepended backslash (\
). This makes it possible to use newlines (\n
), tabs (\t
), and many other special characters in queries.
As a result, backslashes in paths, etc. need to be escaped, too. A Windows path such as C:\Windows
is written in uAQL as "C:\\Windows"
.
Important: Regular expressions require a second round of escaping. To use the path C:\Windows
in a regex, specify it as "C:\\\\Windows"
.
Raw Strings
To avoid having to write so many backslashes, consider using raw string literals, in other words, strings with character escaping disabled. Raw string literals start with a leading r
before the opening quote. The path C:\Windows
would be specified as a raw string like this: r"C:\Windows"
.
Important: regular expressions and the operators like
as well as glob
still require escaping, even with raw strings. To use the path C:\Windows
in a regex or with like
or glob
, specify it as a raw string literal as r"C:\\Windows"
.
Character Escaping Examples
The following uAQL queries demonstrate different ways of matching files in uberAgent’s Config cache
directory.
Using the icontains
operator and a regular string:
Query = icontains(File.Path, "\\vast limits\\uberAgent\\Config cache\\")
<!--NeedCopy-->
Using the like
operator and a raw string:
Query = File.Path like r"%\\vast limits\\uberAgent\\Config cache\\%"
<!--NeedCopy-->
Using the like
operator and a regular string:
Query = File.Path like "%\\\\vast limits\\\\uberAgent\\\\Config cache\\\\%"
<!--NeedCopy-->
Array
Arrays are special datatypes in uAQL that can hold one or multiple integers, booleans, and strings. An array is defined through square brackets: [Element 1, Element 2, Element n]
.
Arrays can be used in queries with the in
operator. The following example tests if the current process name is equal to one of the elements in the array:
Process.Name in ["uberAgent.exe", "explorer.exe", "cmd.exe"]
<!--NeedCopy-->
Keywords
uAQL comes with keywords for all sorts of logical and binary comparisons.
Operator | Description |
---|---|
and , AND
|
Logical AND |
or , OR
|
Logical OR |
not , NOT
|
Logical NOT |
== |
Equality. Case-insensitive for strings. |
=== |
Equality. Case-sensitive for strings. |
!= |
Inequality. Case-insensitive for strings. |
!== |
Inequality. Case-sensitive for strings. |
< |
Less than. |
<= |
Less than or equal. |
> |
Greater than. |
>= |
Greater than or equal. |
in , IN
|
Tests if the value on the left-hand side is equal to any element of the array on the right-hand side. Case-sensitive for strings. |
like , LIKE
|
Pattern matching comparison as in SQL. The following wildcards can be used: The percent sign (% ) matches zero, one, or many characters, including spaces. The underscore (_ ) matches a single character. |
glob , GLOB
|
Uses the Unix file globbing syntax for wildcards. Case-sensitive. |
iregex |
Matches the event property against a regular expression. Case-insensitive. |
regex |
Matches the event property against a regular expression. Case-sensitive. |
iregex_envvars |
Unlike the default regex this version evaluates paths first. See PATH_REGEX for more details. Case-insensitive. Windows only. |
regex_envvars |
Unlike the default regex this version evaluates paths first. See PATH_REGEX for more details. Case-sensitive. Windows only. |
Functions
The following functions can be used in uAQL queries:
Function | Description |
---|---|
strlen(string) |
Returns the length of a string in characters as an integer. |
concat(string, string) |
Concatenates two strings and returns the result. |
lower(string) |
Transforms a string to lowercase and returns the result. |
upper(string) |
Transforms a string to uppercase and returns the result. |
startswith(string, string) |
Returns true if the first string starts with the second string, otherwise false . Case-sensitive. |
istartswith(string, string) |
Returns true if the first string starts with the second string, otherwise false . Case-insensitive. |
endswith(string, string) |
Returns true if the first string ends with the second string, otherwise false . Case-sensitive. |
iendswith(string, string) |
Returns true if the first string ends with the second string, otherwise false . Case-insensitive. |
contains(string, string) |
Returns true if the first string contains the second string, otherwise false . Case-sensitive. |
icontains(string, string) |
Returns true if the first string contains the second string, otherwise false . Case-insensitive. |
join(string array, seperator) |
Returns a concatenated list of all elements from the string array. The separator can contain 0 to n characters. This function is useful when querying results returned by the registry_read_stringmulti function. |
registry_read_binary(hive, key, value) |
Reads a binary value from the registry and returns the data as a hexadecimal string (e.g. 0102FCFF). The hive, key and value parameters are strings. |
registry_read_number(hive, key, value) |
Reads a number from the registry. The hive, key and value parameters are strings. |
registry_read_string(hive, key, value) |
Reads a string from the registry. The hive, key and value parameters are strings. |
registry_read_stringmulti(hive, key, value) |
Reads a multi line string from the registry. The hive, key and value parameters are strings. |
get_env(environment variable name) |
Reads and returns the string value of a given environment variable name. |
set(variable name, value) |
Changes the string value of a given variable. Returns true on success, otherwise false. |
seti(variable name, value) |
Changes the integer value of a given variable. Returns true on success, otherwise false. |
setb(variable name, value) |
Changes the boolean value of a given variable. Returns true on success, otherwise false. |
isnull(variable name) |
Returns true if the given variable name has a null (undefined) value. |
isnull_or_empty(variable name) |
Returns true if the given variable name has a null (undefined) value or if it is an empty string. |
r"string" |
Evaluates a string as raw string, i.e., with character escaping turned off (see above). |
jsonp(variable name, path) |
Accesses values directly from JSON data using a JSON pointer path. Returns the value at the specified path. If the path doesn’t exist, returns null. |
jsonp Function
The jsonp
function is a powerful addition to uAQL that allows you to access values directly from JSON data. It uses a JSON pointer path to navigate through the JSON structure and retrieve specific values. This function is particularly useful when working with complex JSON data structures in your queries.
Syntax
jsonp(variable name, path)
<!--NeedCopy-->
-
variable name
: A variable that contains the JSON data. -
string path
: The JSON pointer path to the desired value.
Examples
Let’s consider the following JSON structure:
{
"user": {
"name": "John Doe",
"age": 30,
"active": true
},
"preferences": {
"theme": "dark",
"notifications": ["email", "sms"]
}
}
<!--NeedCopy-->
Here are some examples of how to use the jsonp
function with this JSON structure:
-
Access a simple property:
jsonp(CEB.EventPayload, "/user/name") == "John Doe" <!--NeedCopy-->
-
Check a boolean value:
jsonp(CEB.EventPayload, "/user/active") == true <!--NeedCopy-->
-
Compare a numeric value:
jsonp(CEB.EventPayload, "/user/age") > 25 <!--NeedCopy-->
-
Check if a value exists in an array:
"email" in jsonp(CEB.EventPayload, "/preferences/notifications") <!--NeedCopy-->
-
Combine with other uAQL functions:
lower(jsonp(CEB.EventPayload, "/preferences/theme")) == "dark" <!--NeedCopy-->
These examples demonstrate how the jsonp
function can be used to navigate and extract data from JSON structures in your uAQL queries. Remember that if the specified path doesn’t exist in the JSON data, the function will return null.
Limitations
While the jsonp
function is powerful, it does have some limitations:
-
Nested Objects in Arrays: The function cannot directly query nested objects within arrays. For example, if you have an array of user objects, you can’t directly query for a specific user’s property.
-
Complex Queries: The function is designed for straightforward JSON navigation. It may not be suitable for complex queries that require filtering or searching within arrays.
Reserved Keywords
All event property names are reserved. Also, operator names including the following keywords are reserved:
Keyword | Description |
---|---|
true , TRUE , True
|
Boolean true constant. |
false , FALSE , False
|
Boolean false constant. |
null , NULL , Null
|
Null (undefined) value. E.g. returned by any registry_read_ function which reads data that does not match the expected registry value type. |
Multiline Queries
To improve the readability of complex and lengthy queries, consider breaking them down into multiple lines. Use QueryStart
to indicate the start of the query and QueryEnd
to indicate its end. Inline comments are valid within the multi-line block of the query, but you should keep them to a single line for clarity and ease of maintenance.
Illustrative example:
[ThreatDetectionRule]
RuleId = 881834a4-6659-4773-821e-1c151789d873
RuleName = Browser-Starts
EventType = Process.Start
Tag = Browser-Starts-By-Users
QueryStart
# Comment about the first line
Process.Name in ["chrome.exe", "firefox.exe"] and
# Comment about the second line
Process.IsSigned == true and
Process.User == "John Doe"
QueryEnd
GenericProperty1 = Process.Name
GenericProperty2 = Process.CommandLine
GenericProperty3 = Process.Path
GenericProperty4 = Process.User
<!--NeedCopy-->
Note: To format an array correctly, ensure that the opening bracket is not the first character of a line. It can be placed at any subsequent position within the same line. For instance:
[ThreatDetectionRule]
RuleId = 881834a4-6659-4773-821e-1c151789d873
RuleName = Browser-Starts
EventType = Process.Start
Tag = Browser-Starts-By-Users
QueryStart
Process.Name in [
"chrome.exe", "firefox.exe"
] and
Process.IsSigned == true and
Process.User == "John Doe"
QueryEnd
GenericProperty1 = Process.Name
<!--NeedCopy-->
The following is an example of invalid array formatting:
[ThreatDetectionRule]
RuleId = 881834a4-6659-4773-821e-1c151789d873
RuleName = Browser-Starts
EventType = Process.Start
Tag = Browser-Starts-By-Users
QueryStart
Process.Name in
["chrome.exe", "firefox.exe"] and
Process.IsSigned == true and
Process.User == "John Doe"
QueryEnd
GenericProperty1 = Process.Name
<!--NeedCopy-->