We are going to take a little break from dashboards this week, though this blog will provide an example chart at the end. In the meantime, we are going to take a look at another function that can help convert data from one type to another. In case you missed earlier blogs on data type conversion, here are a few that we’ve already covered:
There are additional conversions to be performed, one of which is the cast boolean function.
The idea behind the function cast.as_bool is to take an integer or string value and convert it to boolean output. In the case of an integer, the only time the result will return true is if the integer is 1, otherwise the result will be false. For a string, the only value that is returned true is the case-insensitive string true.
Let’s iterate through a search to illustrate this. Our search is looking for user login events while filtering out some chatty logins. We want to create a matrix of different authentication mechanisms used.
In the event section, we are going to use a few different examples to see how cast.as_bool would function based upon the input provided. The $service_auth placeholder variable is using conditional logic (if/then/else) and returning the string true or false depending upon the value in the field extensions.auth.mechanism. Clearly this isn’t using the cast.as_bool function and we are returning a true or false value here, so for our purposes, that might be good enough.
However, let’s define the $network_auth placeholder variable differently and use the cast.as_bool function because our conditional logic is returning the value 1 if the authentication mechanism is the value NETWORK and 0 if it is not.
metadata.event_type = "USER_LOGIN"
principal.hostname != ""
NOT re.regex(target.user.userid, `(\$$|^MSOL|\$@LUNARSTIIINESS\.COM$|^_)`)
$service_auth = if(extensions.auth.mechanism = "SERVICE", "true", "false")
$network_auth = cast.as_bool(if(extensions.auth.mechanism = "NETWORK", 1, 0))
match:
target.user.userid, principal.hostname, $service_auth, $network_auth
outcome:
$auth = array_distinct(extensions.auth.mechanism)
Our match variable is grouping by the user, host and our two placeholder variables. We also defined an outcome variable that is an array of all the values returned for the extensions.auth.mechanism field during our search window that align to the aggregation of the user, hostname, and our two boolean placeholder variables.
Notice in our results that we are returning true and false for both the $service_auth and $network_auth values. cast.as_bool translated the 1 and 0 values to true and false, respectively.
Let’s broaden this search out and add some additional authentication mechanisms to our matrix. I also like to call out pitfalls you may run into so this example will highlight how to use and also how not to use this function.
Changes to the original search are in bold.
metadata.event_type = "USER_LOGIN"
principal.hostname != ""
NOT re.regex(target.user.userid, `(\$$|^MSOL|\$@LUNARSTIIINESS\.COM$|^_)`)
$service_auth = if(extensions.auth.mechanism = "SERVICE", "true", "false")
$network_auth = cast.as_bool(if(extensions.auth.mechanism = "NETWORK", 1, 0))
$remote_interactive = cast.as_bool(if(extensions.auth.mechanism = "REMOTE_INTERACTIVE", "true", "false"))
$userpass_auth_string = if(extensions.auth.mechanism = "USERNAME_PASSWORD", "true", "salami")
$userpass_auth = cast.as_bool(if(extensions.auth.mechanism = "USERNAME_PASSWORD", "true", "salami"))
$unspecified_string = if(extensions.auth.mechanism = "MECHANISM_UNSPECIFIED", "yes", "no")
$unspecified = cast.as_bool(if(extensions.auth.mechanism = "MECHANISM_UNSPECIFIED", "yes", "no"))
match:
target.user.userid, principal.hostname, $service_auth, $network_auth, $remote_interactive, $userpass_auth_string, $userpass_auth, $unspecified_string, $unspecified
outcome:
$auth = array_distinct(extensions.auth.mechanism)
$auth_sum = count(metadata.event_type)
$auth_bool = cast.as_bool(count(metadata.event_type))
We will start with the conditional statement for $remote_interactive which uses the cast.as_bool function with the strings true and false. This is duplicative based on what we found when we built our logic for $service_auth, but I wanted to illustrate it.
The remaining placeholder and outcome variables are pitfalls. For the authentication mechanism of USENAME_PASSWORD, we output two columns, one with the string value and the other with the boolean value based on the application of the cast.as_bool function. Notice that if the value is USERNAME_PASSWORD, we will output true but if not, we output salami. In our boolean output, true is converted to true and salami is converted to false. That may be ok depending upon the use case. For instance, having one value that matches is converted to true and everything else is converted to false.
For the authentication mechanism of MECHANISM_UNSPECIFIED, we used yes or no as the output values in the string. However, when we convert this, everything is false, because cast.as_bool is looking for a 1 in integer or true in string to return true, everything else is false.
Finally, in the outcome section of our search, we generated a count of the authentication events using our match variables. Again, we got values ranging from two to thirty seven. But when converted to boolean, all values were false because the value was not 1.
Now that we’ve covered the syntax and potential pitfalls you may encounter, let’s build a chart for use in a dashboard. We’ve modified our filtering criteria for the principal.hostname and target.user.userid to aggregate common values together. Specifically, we stripped out domain names and converted cases to all lower case and then used these variables in our match section.
metadata.event_type = "USER_LOGIN"
principal.hostname != ""
$hostname = strings.to_lower(strings.substr(principal.hostname,1, strings.length(principal.hostname)-19))
$user = strings.to_lower(target.user.userid)
NOT re.regex(target.user.userid, `(\$$|^MSOL|\$@LUNARSTIIINESS\.COM$|^_)`)
match:
$user, $hostname
outcome:
$service_auth = cast.as_bool(max(if(extensions.auth.mechanism = "SERVICE", 1, 0)))
$network_auth = cast.as_bool(max(if(extensions.auth.mechanism = "NETWORK", 1, 0)))
$remote_interactive = cast.as_bool(max(if(extensions.auth.mechanism = "REMOTE_INTERACTIVE", 1, 0)))
$userpass_auth = cast.as_bool(max(if(extensions.auth.mechanism = "USERNAME_PASSWORD", 1, 0)))
$unspecified = cast.as_bool(max(if(extensions.auth.mechanism = "MECHANISM_UNSPECIFIED", 1, 0)))
$other = cast.as_bool(max(if(extensions.auth.mechanism != "MECHANISM_UNSPECIFIED" and extensions.auth.mechanism != "USERNAME_PASSWORD" and extensions.auth.mechanism != "REMOTE_INTERACTIVE" and extensions.auth.mechanism != "NETWORK" and extensions.auth.mechanism != "SERVICE", 1, 0)))
We then moved the conditional logic statements to the outcome section and made them consistent by using the output of 1 or 0 and applying the aggregation function of max. This gives us a 1 or 0 for each user and hostname pairing. Finally we convert it to true or false with our cast.as_bool function. The last outcome variable is a catch all which will assign a value of true (1) when a value other than the defined column values are used.
Notice how tim.smith_admin and dan.cooper both have multiple types of authentication mechanisms for the specified time range because we are just aggregating on the user and hostname pairing.
I hope this provides a better understanding of the cast.as_bool function and how it can be used in searches, rules and dashboards. There is more to come to assist you when working with your security data in Google SecOps!