Today’s blog will look at another facet of data being incorporated into composite rules. In our previous blog, we extended our rules to include not just detections but added events so that if we have a set of detections that are of interest, but we need an event that is not part of a detection, we can leverage that as well. This time, we are going to take that same concept but leverage data from the entity graph along with our detection.
Entity Graph
The entity graph is a contextual data store within Google Security Operations (SecOps) that stores entities like assets, users, resources, groups and IOCs. By ingesting this data, events are automatically enriched but these entities are also available within the entity graph to join to events. Additionally, the entity graph contains derived contextual data including prevalence, first and last seen and metrics associated with risk analytics. Finally, global threat intelligence resources including Safebrowsing, TOR exit nodes, VirusTotal and more are available for use in rule development.
Before we get into rule writing, let’s take a quick look at our example user, tim.smith_admin. First we will search for the user in the Entity Graph using the search below:
graph.metadata.source_type = "ENTITY_CONTEXT"
graph.metadata.entity_type = "USER"
graph.entity.user.userid = "tim.smith_admin"
While we could have just specified the field that contains “Tim,” the entity graph is quite large when you factor in all of the entities that I mentioned above, it’s generally considered a best practice to include fields like graph.metadata.source_type and graph.metadata.entity_type to make your search more performant. We will be using these fields in our rule in a bit.
In our search results, we can see the entities that match the criteria for the time window specified. When we click on a row, the Event Viewer appears on the right. Contextual data about the entity can be viewed here (as well as added to the tabular view). The portion in the blue box at the top of the event viewer contains values like Department, Employee ID, First and Last Name, Title and more.
In the red box at the bottom, are relations. Relations are how users and groups are associated with one another. There are other relations just the user and group relationship shown here but this is the one we are focusing on today. We can see that Tim is a member of both the Domain Users and Domain Admins groups.
All of this data pertains to our entity, however when it comes to enriching our UDM events, entity data like the department and title are enriched, that is, the content in the blue box but not the relations (red box). BTW, there are loads of additional fields to describe beyond the ones displayed here.
To illustrate this enrichment, let’s run a simple search for the same day and look at process launch events where Tim is the user.
metadata.event_type = "PROCESS_LAUNCH"
principal.user.userid = "tim.smith_admin"
After we execute our search, we can expand the Event Viewer. I’ve shrunk the UDM tree down to just the principal portion of the event. Within the user section, we can see the enriched values from the entity in the event, these are denoted with the E and color coded green. The unenriched values, denoted with U and in white are the ones that originated with our parser at ingest. Fields like principal.user.userid and principal.user.windows_sid are used to link the contextual data in the entity graph and the UDM event.
However, none of the group information in the relationship section is enriched here. That’s fine, but what it does mean is that if we want to use it in our composite rule, we need to tap into the entity graph.
Composite Rules with Entity Graph
With that background on the entity graph, let’s start with a composite rule that should look familiar to you if you’ve been keeping up with this mini-series. This rule relies on non-alerting, single event rules labeled with the tactic of TA0007 or TA0009. These tactics are MITRE AT&CK tactics of Discovery and Collection, respectively. We are joining these detections by a common hostname which our underlying producer rules are aggregated by and we are looking for these events within a six hour window. If we have more than three detections during that time, we will trigger this composite rule.
rule composite_mitre_attack_multi_tactic {
meta:
author = "Google Cloud Security"
description = "Detects multiple detections with a ATT&CK tactic of discovery and collection"
severity = "High"
priority = "High"
type = "composite"
events:
$detect_prod.detection.detection.alert_state = "NOT_ALERTING"
$detect_prod.detection.detection.rule_type = "SINGLE_EVENT"
$detect_prod.detection.detection.rule_labels_"tactic"] = "TA0007" //MITRE ATT&CK Discovery
strings.extract_hostname($detect_prod.detection.detection.detection_fields."hostname"]) = $hostname
$detect_prod1.detection.detection.alert_state = "NOT_ALERTING"
$detect_prod1.detection.detection.rule_type = "SINGLE_EVENT"
$detect_prod1.detection.detection.rule_labelsp"tactic"] = "TA0009" //MITRE ATT&CK Collection
strings.extract_hostname($detect_prod1.detection.detection.detection_fields."hostname"]) = $hostname
match:
$hostname over 6h
outcome:
$risk_score = 60
$total_detections = count_distinct($detect_prod.detection.detection.rule_name)
condition:
$detect_prod and $detect_prod1 and $total_detections > 3
}
For some use cases, this might be sufficient and we can move on with our day, but let’s say that while this is a good start, we want to refine our detection logic to just focus on the domain admins group.
At the top of our detection funnel are the UDM events that were used for our initial detections. The composite rule above used the tactics from those detections. We are going to evolve this by adding in the entity graph to narrow our alerts down to just those in the group domain admin.
Because this rule builds on the previous, I’ve bolded the differences between the original rule and the updated rule that adds the entity graph.
The first thing we did was add the following line to both detections.
$detect_prod.detection.detection.outcomesp"principal_user_userid"] = $user
$detect_prod1.detection.detection.outcomes "principal_user_userid"] = $user
$detect_prod and $detect_prod1 are the event variables that differentiates one detection from the other. Otherwise the field and placeholder variable are the same. These two lines join the detections together. This was not needed previously because we already had a join using the hostname, but because we are joining our detections to the entity graph we need a common value in both the events and entities and this user value is a good choice.
The entire $graph section of the rule points to values in the entity graph. Notice we are using the source and entity type fields like we did in our initial search to narrow the dataset in our rule and improve its overall performance. We are looking for groups with a display name of Domain Admins and notice that we are using the same placeholder variable of $user in the $graph section of the rule to link the entities and events.
rule composite_mitre_attack_multi_tactic_by_domain_admin {
meta:
author = "Google Cloud Security"
description = "Detects multiple detections with a ATT&CK tactic of discovery and collection by a domain admin"
severity = "High"
priority = "High"
type = "composite"
events:
$detect_prod.detection.detection.alert_state = "NOT_ALERTING"
$detect_prod.detection.detection.rule_type = "SINGLE_EVENT"
$detect_prod.detection.detection.rule_labelsE"tactic"] = "TA0007" //MITRE ATT&CK Discovery
$detect_prod.detection.detection.outcomesr"principal_user_userid"] = $user
strings.extract_hostname($detect_prod.detection.detection.detection_fieldsr"hostname"]) = $hostname
$detect_prod1.detection.detection.alert_state = "NOT_ALERTING"
$detect_prod1.detection.detection.rule_type = "SINGLE_EVENT"
$detect_prod1.detection.detection.rule_labelsI"tactic"] = "TA0009" //MITRE ATT&CK Collection
$detect_prod1.detection.detection.outcomesp"principal_user_userid"] = $user
strings.extract_hostname($detect_prod1.detection.detection.detection_fieldse"hostname"]) = $hostname
$graph.graph.metadata.source_type = "ENTITY_CONTEXT"
$graph.graph.metadata.entity_type = "USER"
$graph.graph.entity.user.userid = $user
$graph.graph.relations.relationship = "MEMBER"
$graph.graph.relations.entity_type = "GROUP"
ANY $graph.graph.relations.entity.group.group_display_name = "Domain Admins"
match:
$hostname over 6h
outcome:
$risk_score = 60
$total_detections = count_distinct($detect_prod.detection.detection.rule_name)
condition:
$detect_prod and $detect_prod1 and $graph and $total_detections > 3
}
We could have changed our match variable from hostname to user or perhaps even combined them into a single aggregation, but we have not. We also added the event variable of $graph to the condition section.
When we test our rule, we get the following results. Notice when we expand the composite detection, we have a separate section for the entity. Within the entities section, we can add columns just like we do for events and detections and we have the groups associated with Tim Smith here, including the Domain Admins group.
Here are a couple of tips to keep in mind:
- Like any detection, start small and build out. That is, get your detection working before adding additional data sets, like the entity graph.
- Use SIEM Search to identify the key fields in the entity graph that will be used to filter and join in the rule
- Make sure the placeholder variables are created that provide a method to join the detection(s) to the entity graph
- graph.metadata.source_type and graph.metadata.entity_type will dramatically reduce the size of the join to the entity graph; use them!
- If the value is already enriched to the UDM event, save yourself the time and effort and use the enriched value instead
- If the underlying rules that are used by the composite rule aggregate values in the outcome variables, consider making this aggregated field the match variable instead. For instance, in this rule, we left the match variable as $hostname. If I had many userids in the outcome section, matching on $hostname and $user or just $user would be a better path forward.
While most of the composite rules that we build will just consist of detections, there are times when we may need to leverage other data sets, like the entity graph. Hopefully this blog provides a decent example of how you might go about adding entity data to your detection.