Outcome section syntax

Supported in:

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 outcomeand 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 or all 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 the events section or in the outcome section.
  • The outcome section can't use event variables that have not been defined in the events section.
  • The outcome section can use an event field that was not used in the events section, given that the event variable that the event field belongs to was already defined in the events section.
  • The outcome section can only correlate event variables that have already been correlated in the events 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

Need more help? Get answers from Community members and Google SecOps professionals.