Problem with outcome aggregation (array) for a multiple-event rule

Hello,

I struggle with the outcome section for a rule i'm working on at the moment. I looked in the documentation and on this forum but I could not find any solution.

The rule looks for a potential succesful bruteforce. So I defined 2 events variables : one for failure events, one for success events. Events are aggregated over username and source_ip in the match section. In the outcome section, i would like to be able to summarize all distinct user agents seen in the failure events but also in the success event  in the same outcome variable.

Here is the essential config of my rule:

rule successful_bruteforce {

meta:
name = "Successful Bruteforce"

events:
$fail.metadata.log_type = "AWS_CLOUDTRAIL"
$fail.metadata.event_type = "USER_LOGIN"
$fail.metadata.product_event_type = "ConsoleLogin"
$fail.security_result.action = "BLOCK"
$fail.target.user.user_display_name = $user
$fail.principal.ip = $src_ip

$success.metadata.log_type = "AWS_CLOUDTRAIL"
$success.metadata.event_type = "USER_LOGIN"
$success.metadata.product_event_type = "ConsoleLogin"
$success.security_result.action = "ALLOW"
$success.target.user.user_display_name = $user
$success.principal.ip = $src_ip

match:
$user, $src_ip over 15m

outcome:
$source_ip = array_distinct($src_ip)
$destination_user_name = array_distinct($user)
// Here I can easily aggregate data for both event variables
$event_count = count_distinct($fail.metadata.id)+count_distinct($success.metadata.id)
// /!\ Invalid yara-L syntax but this is what I would like to achieve
$user_agent =
array_distinct($fail.network.http.user_agent, $success.network.http.user_agent)

condition:
#fail > 4 and $success
}

Is this currently a Yara-L limitation or am I doing something wrong ?

Thanks in advance for the help ! ๐Ÿ™‚

 

1 4 93
4 REPLIES 4

The following should work, declaring the useragent as a variable for both conditions, and then calling it via the variable in the outcome section.

rule successful_bruteforce {

  meta:
    name = "Successful Bruteforce"

  events:
    $fail.metadata.log_type = "AWS_CLOUDTRAIL"
    $fail.metadata.event_type = "USER_LOGIN"
    $fail.metadata.product_event_type = "ConsoleLogin"
    $fail.security_result.action = "BLOCK"
    $fail.target.user.user_display_name = $user
    $fail.principal.ip = $src_ip
    $fail.network.http.user_agent = $useragent

    $success.metadata.log_type = "AWS_CLOUDTRAIL"
    $success.metadata.event_type = "USER_LOGIN"
    $success.metadata.product_event_type = "ConsoleLogin"
    $success.security_result.action = "ALLOW"
    $success.target.user.user_display_name = $user
    $success.principal.ip = $src_ip
    $success.network.http.user_agent = $useragent

  match:
    $user, $src_ip over 15m

  outcome:
    $source_ip = array_distinct($src_ip)
    $destination_user_name = array_distinct($user)
    // Here I can easily aggregate data for both event variables
    $event_count = count_distinct($fail.metadata.id) + count_distinct($success.metadata.id)
    // /!\ Invalid yara-L syntax but this is what I would like to achieve
   $user_agent = array_distinct($useragent)

  condition:
    #fail > 4 and $success
}



Hey, thanks for the answer ! The problem with the above solution is that it would tie the two event variables together over the same $useragent value.
Therefore, from my understanding, the rule would not trigger if $success has a different value for $useragent than $fail (for example if the attacker is "randomly" rotating its user-agent) ๐Ÿ˜•

I think at the moment, there are a few partial solutions, but not the one you are looking for.

We could store the success and the fail each in their own outcome variables, like this.

    $success_user_agent = array_distinct($success.network.http.user_agent)
    $failed_user_agent = array_distinct($fail.network.http.user_agent)

You are correct, by using a placeholder variable for user agent, that is effectively a join on that UA being a common field and doesn't solve your use case of the UA being changed mid authentication attempt.

The array and array_distinct in the outcome variable will bind those UA strings values together and we can't apply a function like strings.concat to a list of strings for example, it needs to applied to a string.

While you could use the strings.concat function and essentially create a custom field with constants and strings in them, we still need an aggregation function to enclose this outcome variable. Depending on the number of UA strings, the solution below does run the risk of getting an odd product of combinations of failed UA and success UA combinations. Perhaps this will work for the use case and is preferred to two separate fields.

    $user_agent = array_distinct(strings.concat("Failed UA: ", $fail.network.http.user_agent, " / Success UA: ", $success.network.http.user_agent))

 

 

Hello jstoner,

Thanks for the detailed answer !

Sadly, none of the solutions provided is acceptable regarding my orchestration prerequisites being "one field containing only a list of user-agents" (the orchestration process is already defined and this new rule should respect it). I think that I will only keep the values of $user_agent for the $fail events for the moment as a degraded mode, waiting for an array-concatenating/merging feature to eventually be available.

Another solution that came to my mind :

Since the $fail and $success events are so similar, and given this particular detection logic, it is possible in this particular case to refactor the rule in order to have only one event variable $attempt, introducing a new placeholder $action to store the security_result values, and modifiying the condition like :

  condition:
#attempt > 5 and #action > 1

Regards