Skip to main content
Question

Use metrics functions for Risk Analytics rules | target.user.userid, principal.ip_geo_artifact.network.organization_name

  • March 20, 2026
  • 1 reply
  • 8 views

BriMstone
Forum|alt.badge.img

I have the following rule, but the $historical_threshold_network_success and $historical_threshold_state_success are not counting the historical networks or states, leading to FPs where the detection triggers for user’s where the $historical_threshold_network_success is NOT unique (see attachment image for example). What do I need to do to make $historical_threshold_network_success count correctly? The goal is to reduce FP’s by telling the rule “if specific user has a Duo login history where the ISP (organization) is seen 1 or less times, detect”. This will reduce FPs by baselining user’s typical ISP and only detecting on anomalous ISPs that successfully authenticated. 

 

 

rule Duo_ATO_ActivelyCompromised {

    meta:

        author = "Me"

        description = "Detects a risky blocked Duo login followed by a successful, risky Duo login (potential ATO) from an access device in a state that has not been seen in the last 30 days."

 

    events:

        // Exclude events where the authentication device is in Arizona

        $blocked_login.principal.ip_geo_artifact.location.state != "Arizona"

        $allowed_login.principal.ip_geo_artifact.location.state != "Arizona"

  

        // Event Type 1 - The Blocked Login Attempt

        $blocked_login.metadata.log_type = "DUO_AUTH"

        $blocked_login.metadata.event_type = "USER_LOGIN"

        $blocked_login.security_result.action = "BLOCK"

        $targetAccountId = $blocked_login.target.user.userid

 

        // Look for Duo attack detectors on the blocked login

            $blocked_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "UNREALISTIC_GEOVELOCITY" OR

            $blocked_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "COUNTRY_CODE_MISMATCH" OR

            $blocked_login.extracted.fields["adaptive_trust_assessments.remember_me.reason"] = "Novel Access IP" OR

            $blocked_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "PUSH_HARRASSMENT" OR

            $blocked_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "CONSECUTIVE_FAILURES" AND

            $blocked_login.additional.fields["reason"] = "Low level of trust; detection of one or more known attack patterns."

       

        // Event Type 2 - Allowed Login Attempt

        $allowed_login.metadata.log_type = "DUO_AUTH"

        $allowed_login.metadata.event_type = "USER_LOGIN"

        $allowed_login.security_result.action = "ALLOW"

        $targetAccountId = $allowed_login.target.user.userid

 

        // Look for Duo attack detectors on the allowed login

            $allowed_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "UNREALISTIC_GEOVELOCITY" OR

            $allowed_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "COUNTRY_CODE_MISMATCH" OR

            $allowed_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "PUSH_HARRASSMENT" OR

            $allowed_login.extracted.fields["rbfs_triggered_attacks[0].detector"] = "CONSECUTIVE_FAILURES" OR

            $allowed_login.extracted.fields["adaptive_trust_assessments.remember_me.reason"] = "Novel Access IP" AND

            $allowed_login.additional.fields["reason"] = "Low level of trust; detection of one or more known attack patterns."

 

        // The block must happen before the allowed login

        $blocked_login.metadata.event_timestamp.seconds < $allowed_login.metadata.event_timestamp.seconds

        // Require the same source IP for both events

        $blocked_login.principal.ip = $allowed_login.principal.ip

 

        // Define state variable from the login for metric lookups

        $allowed_login.principal.ip_geo_artifact.location.state = $ip_state

        $allowed_login.principal.ip_geo_artifact.network.organization_name = $ip_network

        //$blocked_login.principal.ip_geo_artifact.location.state = $ip_state

        //$blocked_login.principal.ip_geo_artifact.network.organization_name = $ip_network

       

 

    match:

        $targetAccountId over 1h

 

    outcome:

        $failed_logins_count = count_distinct($blocked_login.metadata.id)

        $user = array_distinct($allowed_login.target.user.userid)

       

        // Baseline: successful logins for this account over the past 30 days

        $historical_login_count = max(metrics.auth_attempts_success(

            period:1d, window:30d,

            metric:event_count_sum, agg:sum,

            target.user.userid:$targetAccountId

        ))

 

        // Baseline: amount of times the source network for the network has been seen in successful logins

        $historical_threshold_network_success = max(metrics.auth_attempts_success(

            period:1d, window:30d,

            metric:event_count_sum, agg:sum,

            principal.ip_geo_artifact.network.organization_name:$ip_network,

            target.user.userid:$targetAccountId

        ))

 

         // Baseline: amount of times the source state has been seen in successful logins

        $historical_threshold_state_success = max(metrics.auth_attempts_success(

            period:1d, window:30d,

            metric:event_count_sum, agg:sum,

            principal.ip_geo_artifact.location.country_or_region:$ip_state,

            target.user.userid:$targetAccountId

        ))

 

        $network_name = array_distinct($allowed_login.principal.ip_geo_artifact.network.organization_name)

        $network_count = count_distinct($allowed_login.principal.ip_geo_artifact.network.organization_name)

        $state_names = array_distinct($allowed_login.principal.ip_geo_artifact.location.state)

 

        //  Risk Score logic

        $risk_score = max(

            if($allowed_login.extracted.fields["rbfs_triggered_attacks[0].detector"] != "", 20, 0) +

            if($allowed_login.extracted.fields["rbfs_triggered_attacks[1].detector"] != "", 40, 0) +

            if($allowed_login.extracted.fields["rbfs_triggered_attacks[2].detector"] != "", 60, 0) +

            if($allowed_login.extracted.fields["rbfs_triggered_attacks[3].detector"] != "", 80, 0) +

            if($blocked_login.extracted.fields["rbfs_triggered_attacks[0].detector"] != "", 5, 0) +

            if($blocked_login.extracted.fields["rbfs_triggered_attacks[1].detector"] != "", 5, 0) +

            if($blocked_login.extracted.fields["rbfs_triggered_attacks[2].detector"] != "", 5, 0) +

            if($blocked_login.extracted.fields["rbfs_triggered_attacks[3].detector"] != "", 5, 0)

        )

 

    condition:

        $blocked_login AND $allowed_login  and $risk_score >= 20 and $historical_login_count >= 10  and $historical_threshold_network_success <= 1  //and $failed_logins_count >= 2

 }

 

 

1 reply

BriMstone
Forum|alt.badge.img
  • Author
  • New Member
  • March 20, 2026

I also tried stripping down the rule so that it is less complicated and the metric count still does not work, you can see the outcome count is still 0, when the alerts make it clear 2 countries are present. 

 

rule ueba_006_anomalous_auth_attempts_different_countries {


 

  meta:

 

    author = "GSO"

    description = "Demonstrates use of num_unique_filter_values by detecting where a user has attempted to authenticate from different countries"

    severity = "Low"

 

  events:

    //$attempt.metadata.event_type = "USER_LOGIN"

    $attempt.metadata.log_type = "DUO_AUTH"

    $attempt.target.user.userid != ""

    $attempt.target.user.userid = $userid

    $attempt.target.user.userid = "<redacted>"

 

   match:

    $userid over 12h

 

  outcome:

    $outcome_variable = max(metrics.auth_attempts_total(

        period: 1d,

        window: 30d,

    // This metric type indicates any filter with a wildcard value should be

    // aggregated over each day to produce a new metric on-the-fly.

        metric: num_unique_filter_values,

        agg: max,

        target.user.userid: $userid,

    // Filter whose value should be counted over each day to produce the

    // num_unique_filter_values metric.

        principal.ip_geo_artifact.location.country_or_region: *

))

 

  condition:

    $attempt

}