Outcome section syntax
The outcome
section of a YARA-L query defines outcome variables that specify the output for a search and dashboard query and additional context and information to a detection when a rule is triggered. These variables can be used for various purposes such as displaying relevant data on dashboards and creating risk scores.
Define outcome section
Use the $
character followed by the variable name to define up to 20 outcome variables (with arbitrary names) in the outcome
section of a single detection query. These outcomes are stored in the detections generated by the query. Each detection may have different values for the outcomes.
Each outcome variable is assigned a value using an expression. This expression can be a UDM field, a placeholder defined in the events section of the query, another outcome variable (already defined), a function, or a conditional statement.
In this query rule, we want to find failed logins from a new location:
rule failed_logins_from_new_location
{
meta:
author = "Security Team"
description = "Detects multiple failed logins for a user from a new, never-before-seen IP address within 10 minutes."
severity = "HIGH"
events:
$e.metadata.event_type = "USER_LOGIN"
$e.security_result.action = "FAIL"
$user = $e.target.user.userid
$ip = $e.principal.ip
outcome:
$failed_login_count = count($e)
$unique_ips = count_distinct($ip)
$first_fail_time = min($e.metadata.event_timestamp)
match:
$user over 10m
condition:
#e >= 5
options:
detection_window=2d
}
</pre>
The outcome
section performs calculations on the event and placeholder variables: count the failed logins, count the distinct IPs, time the failure occurred.
outcome:
$failed_login_count = count($e)
$unique_ips = count_distinct($ip)
$first_fail_time = min($e.metadata.event_timestamp)
If you include and populate the special $risk_score
outcome variable, its value (integer or float) is displayed on the Alerts and IoCs page for alerts generated by the query.
If you don't include a $risk_score
variable in the outcome
section of a query, one of the following default values is set:
- If the query is configured to generate an alert, then
$risk_score
is set to 40. - If the query is not configured to generate an alert, then
$risk_score
is set to 15.
The value of $risk_score
is stored in the security_result.risk_score
UDM field.
Risk score outcome variable
Google SecOps Risk Analytics automatically associates detections and alerts back to entities related to that detection or alert. The risk_score
outcome variable is used to assign an amount of risk. If this value is not set, the default detection or alert value is used. Default values can be configured in settings.
All curated content has an assigned risk_score
value, depending on the severity of the rule. The range used by Google is shown in the following table:
Severity | Score range | Description | Example |
---|---|---|---|
Alerting - Critical | 90 - 100 | Active compromise with the potential to have impact beyond a single user account or endpoint. Requires immediate review. | Mimikatz executed on Domain Controller. |
Alerting - High | 80 - 89 | Active compromise of a single endpoint or entity. Should receive immediate review. | Production server calling out to recent, known C2. |
Alerting - Medium | 50 - 79 | Potential security issue requiring investigation. No confirmed compromise but escalation is possible. | An exposed credential, no signs of misuse. |
Non-Alerting - Low | 20 - 49 | Low-impact security event that, when combined with other indicators or observations, could lead to a more significant incident. No review generally needed, can be combined with other detections through composite rules to create an alert. | Internal port scan. |
Non-Alerting Observations | 1 - 19 | Generally, information based detections intended to build situational awareness of a threat. Generally does not require review; can be combined with other detections through composite rules to generate alerts. | A login event, no signs of misuse. |
Because risk_score
is an outcome variable, your rules can express nuance depending on factors such as threat intelligence or other co-occurant conditions.
The risk_score
set by a rule on a detection or alert is modified by an entity being included in a watchlist before the risk is assigned to the entity, but the risk_score
of the detection or alert does not change.
Entity risk scores can be used to generate risk-based alerts in near real time. For more information Overview of Risk Analytics.
Outcome variable data types
Each outcome variable can have a different data type, which is determined by the expression used to compute it. The following outcome data types are supported in Google SecOps:
- integer
- floats
- string
- lists of integers
- lists of floats
- lists of strings
Conditional logic
You can use conditional logic to compute the value of an outcome. Conditionals are specified using the following syntax pattern:
if(BOOL_CLAUSE, THEN_CLAUSE)
if(BOOL_CLAUSE, THEN_CLAUSE, ELSE_CLAUSE)
You can read a conditional expression as "if BOOL_CLAUSE is true, then return THEN_CLAUSE, else return ELSE_CLAUSE".
BOOL_CLAUSE must evaluate to a boolean value. A BOOL_CLAUSE expression takes a
similar form as expressions in the events
section. For example, it can
contain:
UDM field names with comparison operator:
if($context.graph.entity.user.title = "Vendor", 100, 0)
Placeholder variable that was defined in the
events
section:if($severity = "HIGH", 100, 0)
Another outcome variable defined in the
outcome
section:if($risk_score > 20, "HIGH", "LOW")
Functions that return a boolean:
if(re.regex($e.network.email.from, `.*altostrat.com`), 100, 0)
Look up in a reference list:
if($u.principal.hostname in %my_reference_list_name, 100, 0)
Aggregation comparison:
if(count($login.metadata.event_timestamp.seconds) > 5, 100, 0)
The THEN_CLAUSE and ELSE_CLAUSE must be the same data type. We support integers, floats, and strings.
You can omit the ELSE_CLAUSE if the data type is integer or a float. If omitted, the ELSE_CLAUSE evaluates to 0. For example:
`if($e.field = "a", 5)` is equivalent to `if($e.field = "a", 5, 0)`
You must provide the ELSE_CLAUSE if the data type is string or if the THEN_CLAUSE is a placeholder variable or outcome variable.
Mathematical operations
You can use mathematical operations to compute integer or float data type in the outcome
and events
sections of a query. Google Security Operations supports addition, subtraction, multiplication, division, and modulus as top level operators in a computation.
The following snippet is an example computation in the outcome
section:
outcome:
$risk_score = max(100 + if($severity = "HIGH", 10, 5) - if($severity = "LOW", 20, 0))
Mathematical operations are allowed on the following types of operands as long as each operand and the entire arithmetic expression is properly aggregated (See Aggregations):
- Numeric event fields
- Numeric placeholder variables defined in the
events
section - Numeric outcome variables defined in the
outcome
section - Functions returning ints or floats
- Aggregations returning ints or floats
Modulus is not allowed on floats.
Placeholder variables in outcomes
When computing outcome variables, you can use placeholder variables which were
defined in the events section of your query. In this example, assume that
$email_sent_bytes
was defined in the events section of the rule:
Example: single-event with no match section
// No match section, so this is a single-event query. outcome: // Use placeholder directly as an outcome value. $my_outcome = $email_sent_bytes // Use placeholder in a conditional. $other_outcome = if($file_size > 1024, "SEVERE", "MODERATE") condition: $e
Example: multi-event with match section
match: // This is a multi event query with a match section. $hostname over 5m outcome: // Use placeholder directly in an aggregation function. $max_email_size = max($email_sent_bytes) // Use placeholder in a mathematical computation. $total_bytes_exfiltrated = sum( 1024 + $email_sent_bytes + $file_event.principal.file.size ) condition: $email_event and $file_event
Outcome variables in outcome assignment expressions
Outcome variables can be used to derive other outcome variables, similar to
placeholder variables defined in the events
section. You can refer to an outcome
variable in the assignment of another outcome variable with a $
token followed
by the variable name. Outcome variables must be defined before they can be referenced
in the query text. When used in an assignment expression, outcome variables must
not be aggregated (See Aggregations).
In the following example, the outcome variable $risk_score
derives its
value from the outcome variable $event_count
:
Example: outcome variable derived from another outcome variable
match: // This is a multi event query with a match section. $hostname over 5m outcome: // Aggregates all timestamp on login events in the 5 minute match window. $event_count = count($login.metadata.event_timestamp.seconds) // $event_count cannot be aggregated again. $risk_score = if($event_count > 5, "SEVERE", "MODERATE") // This is the equivalent of the 2 outcomes combined. $risk_score2 = if(count($login.metadata.event_timestamp.seconds) > 5, "SEVERE", "MODERATE") condition: $e
Outcome variables can be used in any type of expression on the right-hand-side of an outcome assignment, except in the following expressions:
- Aggregations
Arrays.length()
function calls- With
any
orall
modifiers
Aggregations
Repeated event fields are non-scalar values. That is, a single variable points to
multiple values. For example, the event field variable $e.target.ip
is a repeated field
and can have zero, one, or many ip values. It is a non-scalar value. Whereas the event field variable
$e.principal.hostname
is not a repeated field and only has 1 value (i.e. a scalar value).
Similarly, both non-repeated event fields and repeated event fields used in the outcome
section
of a query with a match window are non-scalar values.
Example: group events with match section and non-repeated field
The following query groups events using a `match` section and refers to a non-repeated event field in the `outcome` section:
rule OutcomeAndMatchWindow{ ... match: $userid over 5m outcome: $hostnames = array($e.principal.hostname) ... }
Any 5-minute window the query executes over might contain zero, one, or many events. The outcome section
operates on all events in a match window. Any event field variable referred to within the
outcome section can point to zero, one, or many values of the field on each event in the match window.
For example, if a 5-minute window contains 5 $e
events, $e.principal.hostname
in the outcome section points to five different hostnames. The event field variable
$e.principal.hostname
is treated as a non-scalar value in the outcome
section of this query.
Because outcome variables must always yield a single scalar value, any non-scalar value which an outcome assignment depends on must be aggregated to yield a single scalar value. In an outcome section, the following are non-scalar values and must be aggregated:
- Event fields (repeated or non-repeated) when the query uses a
match
section - Event placeholders (repeated or non-repeated) when the query uses a
match
section - Repeated event fields when the query doesn't use a
match
section - Repeated event placeholders when the query doesn't use a
match
section
Scalar event fields, scalar event placeholders, and constants can be wrapped in
aggregation functions in queries that don't include a match
section. However, in
most cases, these aggregations return the wrapped value, making them unnecessary.
An exception is the array()
aggregation, which you can use to explicitly convert
a scalar value into an array.
Outcome variables are treated like aggregations: they must not be re-aggregated when referred to in another outcome assignment.
You can use the following aggregation functions:
Aggregation Function | Description |
---|---|
max() |
Outputs the maximum over all possible values. Only works with integer and float. |
min() |
Outputs the minimum over all possible values. Only works with integer and float. |
sum() |
Outputs the sum over all possible values. Only works with integer and float. |
count_distinct() |
Collects all possible values, then outputs the distinct count of possible values. |
count() |
Behaves like `count_distinct()`, but returns a non-distinct count of possible values. |
array_distinct() |
Collects all possible distinct values, then outputs a list of these values. It truncates the list of distinct values to 1,000 random elements. The deduplication to get a distinct list is applied first, then the truncation is applied. |
array() |
Behaves like array_distinct(), but returns a non-distinct list of values. It also truncates the list of values to 1,000 random elements. |
period_start_for_max() |
Start of the time period where the maximum of the listed value occurred. |
period_start_for_min() |
Start of the time period where the minimum of the listed value occurred. |
The aggregate function is important when a rule includes a condition
section
that specifies multiple events must exist, because the aggregate function will
operate on all the events that generated the detection.
Example: condition for multiple events
The following is an example of a condition for multiple events. If your outcome and condition section contains:
outcome: $asset_id_count = count($event.principal.asset_id) $asset_id_distinct_count = count_distinct($event.principal.asset_id) $asset_id_list = array($event.principal.asset_id) $asset_id_distinct_list = array_distinct($event.principal.asset_id) condition: #event > 1
Since the `condition` section requires there to be more than one `event` for each detection, the aggregate functions will operate on multiple events. Suppose the following events generated one detection:
event: // UDM event 1 asset_id="asset-a" event: // UDM event 2 asset_id="asset-b" event: // UDM event 3 asset_id="asset-b"
Then the values of your outcomes will be:
- $asset_id_count = 3
- $asset_id_distinct_count = 2
- $asset_id_list = `["asset-a", "asset-b", "asset-b"]
- $asset_id_distinct_list = `["asset-a", "asset-b"]
Limitations
- The
outcome
section can't reference a new placeholder variable which wasn't already defined in theevents
section or in theoutcome
section. - The
outcome
section can't use event variables that have not been defined in theevents
section. - The
outcome
section can use an event field that was not used in theevents
section, given that the event variable that the event field belongs to was already defined in theevents
section. - The
outcome
section can only correlate event variables that have already been correlated in theevents
section. Correlations happen when two event fields from different event variables are equated.
See Overview of YARA-L 2.0 for examples of outcome
section.
See Create context-aware analytics for details on detection deduping with the outcome
section.
What's next
Additional information
- Expressions, operators, and constructs used in YARA-L 2.0
- Functions in YARA-L 2.0
- Build composite detection rules
- Examples: YARA-L 2.0 queries
Need more help? Get answers from Community members and Google SecOps professionals.