Skip to main content

In our previous blog, we introduced the fundamentals of composite rules. Before diving into this blog, I’d suggest you take a few minutes and read the earlier blog as we will use terms and concepts that we previously discussed.


The first composite rule that we are going to build is very straightforward to introduce these concepts and put them to work. This composite rule will contain detections created by three producer rules. To help visualize this, we have our UDM events that are used in these three rules and when the producer rules trigger, they write their output to the detections dataset. From there, our composite rule will attempt to find detections with a common match variable of hostname and if found, will generate an alert for an analyst to triage and investigate.



Let's look at one of these producer rules. All three are very similar in the sense that they are rules based on enumeration activities, derived from a CISA Living off the Land pdf. Because many of these events could be benign, we don’t want to alert on these commands being used individually, and we may not even want to alert on just a set of network-centric commands.


While we could write a rather long multi-event rule, this doesn’t provide modularity, so instead we are going to create a set of producer rules that can then be rolled together with a composite rule. The added benefit is these producer rules can also be used for other use cases without having to be rebuilt from the ground up.


Below is the producer network enumeration rule that we are going to trigger when we observe UDM events matching our criteria. We are going to aggregate these events by the principal.hostname when they occur in a 15 minute time range.


rule producer_recon_environment_enumeration_network_cisa_report {

 meta:
   author = "Google Cloud Security"
   description = "Detects network enumeration commands as identified in CISA Living off the Land pdf"
   reference = "https://media.defense.gov/2023/May/24/2003229517/-1/-1/0/CSA_Living_off_the_Land.PDF"
   type = "producer"
   tactic = "TA0007"
   technique = "T1016"
   severity = "Low"
   priority = "Low"

 events:
   (
       $process.metadata.event_type = "PROCESS_LAUNCH" and
       re.regex($process.target.process.command_line, `(|cmd.*/c).*(arp.*-a|curl.*www.ip-api.com|dnscmd.*/enumrecords.*/zone|dnscmd.*/enumzones|dnscmd.*/enumrecords.*/additional|ipconfig.*/all|netsh.*interface.*firewall.*show|netsh.*interface.*portproxy.*show|netsh.*interface.*portproxy.*show.*v4tov4|netsh.*firewall.*show|netsh.*portproxy.*show.*v4tov4|netstat.*-ano)`) nocase
   )
       or
   (
       $process.metadata.event_type = "NETWORK_DNS" and
       $process.network.application_protocol = "DNS" and
       $process.network.dns.questions.name = "www.ip-api.com"
   )
   $process.principal.hostname = $hostname

 match:
   $hostname over 15m

 outcome:
   $risk_score = 10

 condition:
   $process
}

When we test our rule, we can see that we have a number of events that match the regular expression we provided in the events section, aggregated by the principal.hostname. We can save our producer rule if we are happy with our results or tune it further.



With the rule saved, we will start monitoring for these events, but unlike other rules, we probably do not want to alert. Keep in mind, these events alone may not merit an analyst triaging and investigating, so we don’t want this rule to trigger an alert, but we do want it written to the detection dataset, so let’s toggle the Live Rule on, but keep Alerting off.



If we click on the View Rule Detections button at the top of the rule editor, we can view the detection along with its history and metadata. In the example today, we will be using the Rule ID in our composite rule logic. We could use the name of the rule instead, but it is possible to have rules with the same name but each rule will always have a different rule ID, which is why we will be using it instead.



We’ve walked through one producer rule, tested it, toggled it on and found the rule ID. The process for the other two producer rules will follow this same process, except they focus on active directory and system enumeration events.


Now let’s turn our attention to the composite rule.


The format for the composite rule is the same as any other YARA-L rule that you’ve viewed or written, the important point to understand is that it is drawing data from a different dataset. Rather than using UDM event fields, we are using fields from the detection dataset.


The meta section of the rule contains the labels and values that describe the rule, nothing has changed there. The events section uses event variables that are prepended to the events, in this case $detect_prod1, $detect_prod2, and $detect_prod3 because each of the three events have their own unique rule ID. The field where the rule ID is stored is named detection.detection.rule_id so each event variable has its own rule id in the composite detection. 


 events:
   $detect_prod1.detection.detection.rule_id = "ru_3f0eeb91-9b99-41ea-a257-5e9d904205f0" //producer_recon_environment_enumeration_active_directory_cisa_report
   $detect_prod2.detection.detection.rule_id = "ru_ac5e1ee2-a26f-4685-b374-241d840017a1" //producer_recon_environment_enumeration_network_cisa_report
   $detect_prod3.detection.detection.rule_id = "ru_e5d66d09-a326-4184-9158-db3f83ff9f04" //producer_recon_environment_enumeration_system_cisa_report

We need to join these rules together with a common value. For this composite rule, we want to know when these three detections were observed on the same host, so we are going to use that value to join these detections.


Fortunately in our producer rules, we used the hostname in our match section as the aggregated value so our join will be straightforward. However, because we are working with the detection dataset rather than UDM events, we need a way to represent that common value. The detection dataset stores the match variables from a rule in the field detection.detection.detection_fields as a key/value pair. It can be retrieved using syntax like this:


detection.detection.detection_fieldsd"<match variable name>"]

So, we can add to the event section of our rule a placeholder variable of $hostname that will join these three rules when a common match variable from the three producer rules is found, like this.


 


 events:
   $detect_prod1.detection.detection.rule_id = "ru_3f0eeb91-9b99-41ea-a257-5e9d904205f0" //producer_recon_environment_enumeration_active_directory_cisa_report
   $detect_prod1.detection.detection.detection_fieldsd"hostname"] = $hostname

   $detect_prod2.detection.detection.rule_id = "ru_ac5e1ee2-a26f-4685-b374-241d840017a1" //producer_recon_environment_enumeration_network_cisa_report
   $detect_prod2.detection.detection.detection_fieldsd"hostname"] = $hostname

   $detect_prod3.detection.detection.rule_id = "ru_e5d66d09-a326-4184-9158-db3f83ff9f04" //producer_recon_environment_enumeration_system_cisa_report
   $detect_prod3.detection.detection.detection_fieldsd"hostname"] = $hostname

With that placeholder variable created to join our detections together, we can also use that variable in the match section to aggregate these events and only trigger the composite rule if all three detections have been observed within the two hour time window we are using in this example.


 match:
   $hostname over 2h

The outcome section contains outcome variables like risk score and other calculated output based on the composite rule. In this case, we assigned a risk score of 60 to this composite rule as well as an array of the names of the rules triggered. The rule names are carried into the results, but perhaps you would like a field with these values stored in an array to export to another platform. The example below shows how you might achieve this.


 outcome:
   $risk_score = 60
   $rules_triggered = arrays.concat(arrays.concat(array_distinct($detect_prod1.detection.detection.rule_name),    array_distinct($detect_prod2.detection.detection.rule_name)),array_distinct($detect_prod3.detection.detection.rule_name))

Finally, the condition section of the rule contains the three event variables that we used in the events section because we need all three detections to exist in our rule to trigger it.


 condition:
   $detect_prod1 and $detect_prod2 and $detect_prod3

Putting it all together, here is our complete rule.


 


rule composite_lolbin_recon_by_hostname {

 meta:
   author = "Google Cloud Security"
   description = "Detects multiple producer rules triggering based on a common tactic of discovery associated with Volt Typhoon"
   reference = "https://media.defense.gov/2023/May/24/2003229517/-1/-1/0/CSA_Living_off_the_Land.PDF"
   severity = "High"
   priority = "High"
   type = "composite"

 events:
   $detect_prod1.detection.detection.rule_id = "ru_3f0eeb91-9b99-41ea-a257-5e9d904205f0" //producer_recon_environment_enumeration_active_directory_cisa_report
   $detect_prod1.detection.detection.detection_fieldsd"hostname"] = $hostname

   $detect_prod2.detection.detection.rule_id = "ru_ac5e1ee2-a26f-4685-b374-241d840017a1" //producer_recon_environment_enumeration_network_cisa_report
   $detect_prod2.detection.detection.detection_fieldsd"hostname"] = $hostname
      
   $detect_prod3.detection.detection.rule_id = "ru_e5d66d09-a326-4184-9158-db3f83ff9f04" //producer_recon_environment_enumeration_system_cisa_report
   $detect_prod3.detection.detection.detection_fieldsd"hostname"] = $hostname

 match:
   $hostname over 2h

 outcome:
   $risk_score = 60
   $rules_triggered = arrays.concat(arrays.concat(array_distinct($detect_prod1.detection.detection.rule_name),    array_distinct($detect_prod2.detection.detection.rule_name)),array_distinct($detect_prod3.detection.detection.rule_name))

 condition:
   $detect_prod1 and $detect_prod2 and $detect_prod3
}

When we test our rule, we can see that our composite rule triggers and we can expand the rule to uncover the specific detections. Notice that we have four detections, one of the producer rules triggered twice within that two hour window that the composite detection specified.


We can also expand the detection and view the underlying UDM events that are associated with it. The columns button on the upper right side of the page provides the user the ability to add additional fields and associated values to the view, both at the detection and alert level, but also at the event level.



If you are happy with the composite rule, use the rule editor and toggle the Live Rule and Alerting options on. This is just one example of how composite rules can be used. We will continue to provide additional examples but remember that producer rules need to be created and set to Live and we need common values to join detections together, so think about what you want those common values to be, and then use them to join composite rules together.


I hope this provides you with a good foundation to start exploring composite rules!

Be the first to reply!

Reply