UEBA - A Key Detection Ingredient

adbarnett
Staff

Gartner coined the term User and Entity Behavior Analytics (UEBA) in 2015, sparking a flood of 'silver bullet' products. A decade later, effective UEBA implementation remains a challenge for most organizations. Personally, I’ve always seen UEBA as a technology to prioritize and refine security alerts, not a standalone solution.

I’d like to discuss how to implement UEBA with the objective of reducing the volume of security alerts that could otherwise overwhelm a security team.

If you'd like to dive deeper into the foundational concepts for what is discussed here, I recommend checking out these three previous articles: New to Google SecOps: Using Metrics in YARA-L Rules (Part 1), (Part 2) and (Part 3)

Starting Rule

The below starting point YL2 rule (Azure command interpreter login after multiple failures) for our scenario identifies the combination of two main events to generate an alert:

  1. Identifying a single account with a sequence of at least nine failed Azure login attempts within the 4 hour match window regardless of the specific Azure service.
  2. Verifies that the same account performed a successful login on either Azure CLI, Azure Active Directory PowerShell, or Azure PowerShell within the same 4 hour window.

This approach, while providing valuable information, inherently lacks the desired fidelity by including events where an account owner may have simply forgotten their password and performs a login that matches their normal activity after failing a few times. To increase our confidence that malicious behavior is occurring with such a rule, we can integrate User and Entity Behavior Analytics (UEBA). UEBA can transform a high-risk action into a truly actionable alert.

  events:
    $blocked_login.metadata.vendor_name = "Microsoft"
    $blocked_login.metadata.log_type = "AZURE_AD"
    $blocked_login.metadata.event_type = "USER_LOGIN"
    $blocked_login.security_result.action = "BLOCK"
    $blocked_login.security_result.summary = "Failed login occurred"
//Error 50140 - This error occurrs due to "Keep me signed in" interrupt when the user was signing-in.This can occur conjunction with succesful logins
    $blocked_login.security_result.rule_id != "50140" 
    $allowed_login.metadata.vendor_name = "Microsoft"
    $allowed_login.metadata.log_type = "AZURE_AD"
    $allowed_login.metadata.event_type = "USER_LOGIN"
    $allowed_login.security_result.action = "ALLOW"
    (
      $allowed_login.target.application = "Microsoft Azure PowerShell" or
      $allowed_login.target.application = "Microsoft Azure CLI" or
      $allowed_login.target.application = "Azure Active Directory PowerShell"
    )
    $targetAccountId = $blocked_login.target.user.userid
    $targetAccountId = $allowed_login.target.user.userid
    $blocked_login.metadata.event_timestamp.seconds < $allowed_login.metadata.event_timestamp.seconds

  match:
    $targetAccountId over 4h 

  outcome:
    $failed_logins_count = count_distinct($blocked_login.metadata.id)
    $victim_name = array_distinct($blocked_login.target.user.userid)
    $failed_logins_count_threshold = 9

  condition:
    $blocked_login and $allowed_login and $failed_logins_count >= 9

Applying UEBA as a Detection Ingredient

To improve the efficiency of the original rule with the goal of identifying suspicious or malicious account behavior, we will insert two separate UEBA analytics.

$historical_login_count - Have we seen this user login before? If so, how many times in the last month? We look back over the last 30 days, and event_count_sum tells us how many times such logins were observed, each day, over that window. We then apply the sum aggregation, to get a total count of the number of times this has been observed in the past month.

$historical_threshold_country_success - We might also want to consider if this is the first time that this particular user has logged in from this country. Again, looking back over the past 30 days, we look at the first_seen metric from each day. If any of those values is greater than 0, that means we’ve seen that activity before (we do this by checking if the max aggregation is greater than 0). This is performed on each event where the principal.ip_geo_artifact.location.country_or_region and target.user.userid fields match.

By implementing these two analytics we are able to reduce false positives by ensuring the account has a pattern of being used at least 10 times and the login source country for the account has not been seen in successful logins over a 30 day period. Certain events will require more or less comprehensive historical baselines based on the activity being performed. By using 10 events within a 30 day period we will have a well established pattern of activity to compare against.

This is defined in the outcome section by ensuring the analytic outcomes are:

$historical_login_count >=10 and 

$historical_threshold_country_success = 0

 

  events:
    $blocked_login.metadata.vendor_name = "Microsoft"
    $blocked_login.metadata.log_type = "AZURE_AD"
    $blocked_login.metadata.event_type = "USER_LOGIN"
    $blocked_login.security_result.action = "BLOCK"
    $blocked_login.security_result.summary = "Failed login occurred"
//Error 50140 - This error occurrs due to "Keep me signed in" interrupt when the user was signing-in. This can occur conjunction with succesful logins.
    $blocked_login.security_result.rule_id != "50140"
    $allowed_login.metadata.vendor_name = "Microsoft"
    $allowed_login.metadata.log_type = "AZURE_AD"
    $allowed_login.metadata.event_type = "USER_LOGIN"
    $allowed_login.security_result.action = "ALLOW"
    (
      $allowed_login.target.application = "Microsoft Azure PowerShell" or
      $allowed_login.target.application = "Microsoft Azure CLI" or
      $allowed_login.target.application = "Azure Active Directory PowerShell"
    )
    $targetAccountId = $blocked_login.target.user.userid
    $targetAccountId = $allowed_login.target.user.userid
    $blocked_login.metadata.event_timestamp.seconds < $allowed_login.metadata.event_timestamp.seconds

  match:
    $targetAccountId over 4h

  outcome:
    $failed_logins_count = count_distinct($blocked_login.metadata.id)
    $victim_name = array_distinct($blocked_login.target.user.userid)
    $failed_logins_count_threshold = 9
 // Counts the number of successful logins for this account over the past 30 days,this is helped to establish a baseline of activity
   $historical_login_count = max(metrics.auth_attempts_success(
     period:1d, window:30d,
     metric:event_count_sum, agg:sum,
     target.user.userid:$user_id))

 // Analyzes historical login activity to determine the amount of times the source country for the IP address has been seen in successful logins.
   $historical_threshold_country_success = max(metrics.auth_attempts_success(
     period:1d, window:30d,
     metric:first_seen, agg:max,
     principal.ip_geo_artifact.location.country_or_region:$ip_country, target.user.userid:$user_id))

  condition:
    $blocked_login and $allowed_login and $failed_logins_count >= 9 and $historical_login_count >=10 and $historical_threshold_country_success = 0

Wrapping Up

The same approach used in this blog can be implemented across a wide range of detections to create high fidelity alerting out of your existing rule sets.

Some other examples of this are:

  • Looking for data transfer anomalies after exploitation attempts on public facing systems.
  • Building DDoS detection for custom or proprietary applications using network flow totals, DNS activity or HTTP query rate anomalies.
  • Identifying post exploitation reconnaissance with resource read failure anomalies.

These are just a few examples of ways to implement UEBA analytics and you should always keep in mind that multiple UEBA analytics can be placed within a single rule to further increase the accuracy of the detection.

5 1 21K
Authors