Detection rules › YARA-L
YARA-L rules: 0a
AWS IAM Access Denied Discovery Events
#Detects excessive AccessDenied events within an hour timeframe from an IAM User
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule aws_iam_access_denied_discovery_events {
meta:
author = "Google Cloud Security"
description = "Detects excessive AccessDenied events within an hour timeframe from an IAM User"
rule_id = "mr_a175b0d9-4488-46cc-880a-2408cb301f41"
rule_name = "AWS IAM Access Denied Discovery Events"
mitre_attack_tactic = "Discovery"
mitre_attack_technique = "Cloud Infrastructure Discovery"
mitre_attack_url = "https://attack.mitre.org/techniques/T1580/"
mitre_attack_version = "v13.1"
type = "Alert"
data_source = "AWS CloudTrail"
platform = "AWS"
severity = "Low"
priority = "Low"
events:
$cloudtrail.metadata.vendor_name = "AMAZON"
$cloudtrail.metadata.product_name = "AWS CloudTrail"
$cloudtrail.security_result.rule_id = "AccessDenied"
$cloudtrail.security_result.action = "BLOCK"
$cloudtrail.principal.user.userid = $user_id
$cloudtrail.target.application = $target_application
$cloudtrail.metadata.product_event_type = $product_event_type
match:
$user_id over 1h
outcome:
$risk_score = max(35)
$mitre_attack_tactic = "Discovery"
$mitre_attack_technique = "Cloud Infrastructure Discovery"
$mitre_attack_technique_id = "T1580"
$event_count = count_distinct($cloudtrail.metadata.id)
$network_http_user_agent = array_distinct($cloudtrail.network.http.user_agent)
$principal_ip = array_distinct($cloudtrail.principal.ip)
$principal_ip_country = array_distinct($cloudtrail.principal.ip_geo_artifact.location.country_or_region)
$principal_ip_state = array_distinct($cloudtrail.principal.ip_geo_artifact.location.state)
$principal_user_display_name = array_distinct($cloudtrail.principal.user.user_display_name)
$recipient_aws_account_id = array_distinct($cloudtrail.additional.fields["recipientAccountId"])
$aws_region = array_distinct($cloudtrail.principal.location.name)
$target_resource_name = array_distinct($cloudtrail.target.resource.name)
$target_resource_product_object_id = array_distinct($cloudtrail.target.resource.product_object_id)
$security_result_description = array_distinct($cloudtrail.security_result.description)
$target_applications = array_distinct($cloudtrail.target.application)
$product_event_types = array_distinct($cloudtrail.metadata.product_event_type)
condition:
#cloudtrail > 5 and #target_application > 1 and #product_event_type > 1
}
YARA-L Rule Structure
Condition
Fires when $cloudtrail occurs more than 5 times in the 1h window and $target_application occurs more than 1 times in the 1h window and $product_event_type occurs more than 1 times in the 1h window.
Events
$cloudtrail
security_result.rule_id = "AccessDenied"security_result.action = "BLOCK"
Match and Correlation
Outcome
Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.
| Field | Expression |
|---|---|
risk_score | max(35) |
event_count | count_distinct($cloudtrail.metadata.id) |
network_http_user_agent | array_distinct($cloudtrail.network.http.user_agent) |
principal_ip | array_distinct($cloudtrail.principal.ip) |
principal_ip_country | array_distinct($cloudtrail.principal.ip_geo_artifact.location.country_or_region) |
principal_ip_state | array_distinct($cloudtrail.principal.ip_geo_artifact.location.state) |
principal_user_display_name | array_distinct($cloudtrail.principal.user.user_display_name) |
recipient_aws_account_id | array_distinct($cloudtrail.additional.fields["recipientAccountId"]) |
aws_region | array_distinct($cloudtrail.principal.location.name) |
target_resource_name | array_distinct($cloudtrail.target.resource.name) |
target_resource_product_object_id | array_distinct($cloudtrail.target.resource.product_object_id) |
security_result_description | array_distinct($cloudtrail.security_result.description) |
target_applications | array_distinct($cloudtrail.target.application) |
product_event_types | array_distinct($cloudtrail.metadata.product_event_type) |
GitHub Enterprise Audit Log Stream Destroyed
#Detects when a GitHub audit log stream is destroyed. An adversary may attempt to destroy audit logs to erase their tracks.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| GitHub | Audit log action audit_log_streaming.destroy: Audit log streaming endpoint deleted |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
// Copyright 2024 Google LLC. Licensed under Apache-2.0.
rule github_enterprise_audit_log_stream_destroyed {
meta:
author = "Google Cloud Security"
description = "Detects when a GitHub audit log stream is destroyed. An adversary may attempt to destroy audit logs to erase their tracks."
rule_id = "mr_f86cc19d-4cbb-4ba5-97ef-1d648d53d06a"
rule_name = "GitHub Enterprise Audit Log Stream Destroyed"
assumption = "Your GitHub enterprise audit log settings are configured to log the source IP address for events. Reference: https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/displaying-ip-addresses-in-the-audit-log-for-your-organization"
type = "alert"
severity = "High"
priority = "High"
platform = "GitHub"
data_source = "github"
mitre_attack_tactic = "Defense Evasion"
mitre_attack_technique = "Impair Defenses: Disable or Modify Cloud Logs"
mitre_attack_url = "https://attack.mitre.org/versions/v14/techniques/T1562/008/"
mitre_attack_version = "v14"
reference = "https://docs.github.com/en/enterprise-cloud@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/audit-log-events-for-your-enterprise"
events:
$github.metadata.vendor_name = "GITHUB" nocase
$github.metadata.product_name = "GITHUB"
$github.metadata.product_event_type = "audit_log_streaming.destroy"
outcome:
$risk_score = max(85)
$mitre_attack_tactic = "Defense Evasion"
$mitre_attack_technique = "Impair Defenses: Disable or Modify Cloud Logs"
$mitre_attack_technique_id = "T1562.008"
$principal_ip = array_distinct($github.principal.ip)
$principal_user_userid = array_distinct($github.principal.user.userid)
$principal_ip_country = array_distinct($github.principal.ip_geo_artifact.location.country_or_region)
$principal_ip_state = array_distinct($github.principal.ip_geo_artifact.location.state)
$principal_ip_city = array_distinct($github.principal.location.city)
$security_result_summary = array_distinct($github.security_result.summary)
condition:
$github
}
YARA-L Rule Structure
Condition
Fires when at least one $github event.
Events
$github
metadata.product_event_type = "audit_log_streaming.destroy"
Outcome
Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.
| Field | Expression |
|---|---|
risk_score | max(85) |
principal_ip | array_distinct($github.principal.ip) |
principal_user_userid | array_distinct($github.principal.user.userid) |
principal_ip_country | array_distinct($github.principal.ip_geo_artifact.location.country_or_region) |
principal_ip_state | array_distinct($github.principal.ip_geo_artifact.location.state) |
principal_ip_city | array_distinct($github.principal.location.city) |
security_result_summary | array_distinct($github.security_result.summary) |
Low Prevalence Hash On Process Launch Low Prevalence Domain Accessed
#Detects when a low prevalence hash is seen during a process launch event, and a low prevalence domain is accessed within 10m.
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 1: Process creation |
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule low_prevalence_hash_on_process_launch_low_prevalence_domain_accessed {
meta:
author = "Google Cloud Security"
description = "Detects when a low prevalence hash is seen during a process launch event, and a low prevalence domain is accessed within 10m."
rule_id = "mr_0624f9fb-8c73-4b8c-8651-b88ed736c862"
rule_name = "Low Prevalence Hash On Process Launch Low Prevalence Domain Accessed"
type = "alert"
tags = "prevalence"
data_source = "zscalar, microsoft sysmon"
severity = "Low"
priority = "Low"
events:
// Process Launch
$process.metadata.event_type = "PROCESS_LAUNCH"
$process.principal.ip = $ip
$process.target.file.sha256 != ""
$process.target.file.sha256 = $sha256
// File Prevalence Criteria
$fileprev.graph.metadata.entity_type = "FILE"
$fileprev.graph.metadata.source_type = "DERIVED_CONTEXT"
$fileprev.graph.entity.file.prevalence.day_count = 10
$fileprev.graph.entity.file.prevalence.rolling_max > 0
//Adjust rolling_max for a different threshold as needed
$fileprev.graph.entity.file.prevalence.rolling_max <= 2
//Join process event to file prevalence entity
$fileprev.graph.entity.file.sha256 = $sha256
// Network HTTP
$http.metadata.event_type = "NETWORK_HTTP"
$http.principal.ip = $ip
$http.target.hostname = $hostname
// Filter out URLs with RFC 1918 IP addresses
not net.ip_in_range_cidr($http.target.ip, "127.0.0.1/32")
not net.ip_in_range_cidr($http.target.ip, "10.0.0.0/8")
not net.ip_in_range_cidr($http.target.ip, "172.16.0.0/12")
not net.ip_in_range_cidr($http.target.ip, "192.168.0.0/16")
// Only match valid FQDN, filter out background non-routable noise
re.regex($http.target.hostname, `(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]`)
// Domain Prevalence Criteria
$domainprev.graph.metadata.entity_type = "DOMAIN_NAME"
$domainprev.graph.metadata.source_type = "DERIVED_CONTEXT"
$domainprev.graph.entity.domain.prevalence.day_count = 10
$domainprev.graph.entity.domain.prevalence.rolling_max > 0
// Adjust rolling_max for a different threshold as needed
$domainprev.graph.entity.domain.prevalence.rolling_max < 2
// Join network event to domain entity
$domainprev.graph.entity.domain.name = $hostname
// Apply timing element to force the process to occur before the http
$process.metadata.event_timestamp.seconds <= $http.metadata.event_timestamp.seconds
match:
$ip over 10m
outcome:
$risk_score = max(
// increment risk score based upon rolling_max prevalence
if ( $domainprev.graph.entity.domain.prevalence.rolling_max >= 10, 10) +
if ( $domainprev.graph.entity.domain.prevalence.rolling_max >= 2 and $domainprev.graph.entity.domain.prevalence.rolling_max <= 9 , 20) +
if ( $domainprev.graph.entity.domain.prevalence.rolling_max = 1, 30)
)
// added to populate alert graph with additional context
// Commented out principal.ip because it is already represented in graph as match variable. If match changes, can uncomment to add to results
//$principal_ip = array_distinct($http.principal.ip)
$target_ip = array_distinct($http.target.ip)
$principal_process_pid = array_distinct($process.principal.process.pid)
$principal_process_command_line = array_distinct($process.principal.process.command_line)
$principal_process_file_sha256 = array_distinct($process.principal.process.file.sha256)
$principal_process_file_full_path = array_distinct($process.principal.process.file.full_path)
$principal_process_product_specific_process_id = array_distinct($process.principal.process.product_specific_process_id)
$principal_process_parent_process_product_specific_process_id = array_distinct($process.principal.process.parent_process.product_specific_process_id)
$target_process_pid = array_distinct($process.target.process.pid)
$target_process_command_line = array_distinct($process.target.process.command_line)
$target_process_file_sha256 = array_distinct($process.target.process.file.sha256)
$target_process_file_full_path = array_distinct($process.target.process.file.full_path)
$target_process_product_specific_process_id = array_distinct($process.target.process.product_specific_process_id)
$principal_user_userid = array_distinct($process.principal.user.userid)
$target_url = array_distinct($http.target.url)
condition:
$process and $fileprev and $http and $domainprev
}
YARA-L Rule Structure
Condition
Fires when at least one $process event in the 10m window and $fileprev matches entity context and at least one $http event in the 10m window and $domainprev matches entity context.
Events
$domainprev
graph.metadata.entity_type = "DOMAIN_NAME"graph.metadata.source_type = "DERIVED_CONTEXT"graph.entity.domain.prevalence.day_count = "10"graph.entity.domain.prevalence.rolling_max > "0"graph.entity.domain.prevalence.rolling_max < "2"
$fileprev
graph.metadata.entity_type = "FILE"graph.metadata.source_type = "DERIVED_CONTEXT"graph.entity.file.prevalence.day_count = "10"graph.entity.file.prevalence.rolling_max > "0"graph.entity.file.prevalence.rolling_max <= "2"
$process
target.file.sha256 is not null
$http
target.ip cidr_match "127.0.0.1/32"target.ip cidr_match "10.0.0.0/8"target.ip cidr_match "172.16.0.0/12"target.ip cidr_match "192.168.0.0/16"target.hostname matches "(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]"
Match and Correlation
Outcome
Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.
| Field | Expression |
|---|---|
risk_score | max( if ( $domainprev.graph.entity.domain.prevalence.rolling_max >= 10, 10) + if ( $domainprev.graph.entity.domain.prevalence.rolling_max >= 2 and $domainprev.graph.entity.domain.prevalence.rolling_max <= 9 , 20) + if ( $domainprev.graph.entity.domain.prevalence.rolling_max = 1, 30) ) |
target_ip | array_distinct($http.target.ip) |
principal_process_pid | array_distinct($process.principal.process.pid) |
principal_process_command_line | array_distinct($process.principal.process.command_line) |
principal_process_file_sha256 | array_distinct($process.principal.process.file.sha256) |
principal_process_file_full_path | array_distinct($process.principal.process.file.full_path) |
principal_process_product_specific_process_id | array_distinct($process.principal.process.product_specific_process_id) |
principal_process_parent_process_product_specific_process_id | array_distinct($process.principal.process.parent_process.product_specific_process_id) |
target_process_pid | array_distinct($process.target.process.pid) |
target_process_command_line | array_distinct($process.target.process.command_line) |
target_process_file_sha256 | array_distinct($process.target.process.file.sha256) |
target_process_file_full_path | array_distinct($process.target.process.file.full_path) |
target_process_product_specific_process_id | array_distinct($process.target.process.product_specific_process_id) |
principal_user_userid | array_distinct($process.principal.user.userid) |
target_url | array_distinct($http.target.url) |
sap multi terminal logon
#Identifies successful logons for the same User ID from multiple different terminal IDs or client hostnames in a short timeframe, indicating potential credential sharing or session theft.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Privilege Escalation | |
| Stealth |
Telemetry Coverage
Rule Body
// Copyright 2026 Google LLC. Licensed under Apache-2.0.
rule sap_multi_terminal_logon {
meta:
author = "Google Cloud Security"
description = "Identifies successful logons for the same User ID from multiple different terminal IDs or client hostnames in a short timeframe, indicating potential credential sharing or session theft."
severity = "medium"
tactic = "TA0006"
technique = "T1078"
events:
$e.metadata.log_type = "SAP_SECURITY_AUDIT"
(
$e.metadata.event_type = "USER_LOGIN" or
$e.additional.fields["msg_1"] = /^AU1$|^AU5$/
)
// Ensure we only check events that actually have a terminal/hostname recorded
$e.principal.hostname != ""
$terminal = $e.principal.hostname
$user = $e.principal.user.userid
match:
// You can reduce this window to 15m or 30m if 1h is too broad for direct terminal switches
$user over 15m
outcome:
$terminals = array_distinct($terminal)
$count_of_terminals = count_distinct($terminal)
// Give a high risk score if they switch terminals
$risk_score = 60
$sap_instance = array_distinct($e.target.resource.name)
condition:
// Trigger if the user is seen on 2 or more distinct terminals
#terminal >= 2
}
YARA-L Rule Structure
Condition
Fires when $terminal occurs at least 2 times in the 15m window.
Events
$e
metadata.log_type = "SAP_SECURITY_AUDIT"additional.fields["msg_1"] matches "^AU1$|^AU5$"principal.hostname is not null
Match and Correlation
Outcome
Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.
| Field | Expression |
|---|---|
terminals | array_distinct($terminal) |
count_of_terminals | count_distinct($terminal) |
risk_score | 60 |
sap_instance | array_distinct($e.target.resource.name) |
sap security audit log user created deleted or unlocked
#Detects when an SAP user is created, deleted, or unlocked
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
// Copyright 2026 Google LLC. Licensed under Apache-2.0.
rule sap_security_audit_log_user_created_deleted_or_unlocked {
meta:
author = "Google Cloud Security"
description = "Detects when an SAP user is created, deleted, or unlocked"
severity = "Medium"
tactic = "TA0003"
technique = "T1136"
events:
$e.metadata.log_type = "SAP_SECURITY_AUDIT"
$e.additional.fields["msg_1"] = /^AU7$|^AU8$|^AU9$|^AUA$/ nocase
$user = $e.principal.user.userid
$sid = $e.target.application
//exclusions
not $user in %sap_admin_users.user
match:
$user, $sid over 59m
outcome:
$risk_score = 75
$target_sid = array_distinct($sid)
$description = array_distinct($e.metadata.description)
$terminal = array_distinct($e.principal.hostname)
$message_ids = array_distinct($e.additional.fields["msg_1"])
$target_user = array_distinct($e.additional.fields["param1_1"])
$t_code = array_distinct($e.security_result.detection_fields["slgtc_1"])
$TXSUBCLSID = array_distinct($e.security_result.summary)
$system = array_distinct($e.target.application)
$instance = array_distinct($e.target.resource.name)
condition:
$e
}
YARA-L Rule Structure
Condition
Fires when at least one $e event in the 59m window.
Events
$e
metadata.log_type = "SAP_SECURITY_AUDIT"additional.fields["msg_1"] matches "^AU7$|^AU8$|^AU9$|^AUA$"principal.user.userid in "%sap_admin_users.user"
Match and Correlation
Outcome
Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.
| Field | Expression |
|---|---|
risk_score | 75 |
target_sid | array_distinct($sid) |
description | array_distinct($e.metadata.description) |
terminal | array_distinct($e.principal.hostname) |
message_ids | array_distinct($e.additional.fields["msg_1"]) |
target_user | array_distinct($e.additional.fields["param1_1"]) |
t_code | array_distinct($e.security_result.detection_fields["slgtc_1"]) |
TXSUBCLSID | array_distinct($e.security_result.summary) |
system | array_distinct($e.target.application) |
instance | array_distinct($e.target.resource.name) |