Detection rules › YARA-L
YARA-L rules: 1v
| Rule | Severity |
|---|---|
| GCP Free Gmail Domains Added To IAM Policy | high |
| GitHub Outgoing Organization Transfer Initiated | low |
| Okta User Logins From Multiple Cities | medium |
| VT Relationships File Contacts Domain | high |
GCP Free Gmail Domains Added To IAM Policy
#Detect when free Google Email addressess are added to IAM Policy such as gmail.com, googlemail.com or googlegroups.com.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
// Copyright 2024 Google LLC. Licensed under Apache-2.0.
rule gcp_free_gmail_domains_added_to_iam_policy {
meta:
author = "Google Cloud Security"
description = "Detect when free Google Email addressess are added to IAM Policy such as gmail.com, googlemail.com or googlegroups.com."
rule_id = "mr_eec293b2-75d6-48f1-871d-182d67345a9c"
rule_name = "GCP Free Gmail Domains Added To IAM Policy"
mitre_attack_tactic = "Persistence"
mitre_attack_technique = "Create Account: Cloud Account"
mitre_attack_url = "https://attack.mitre.org/techniques/T1136/003/"
mitre_attack_version = "v14.1"
type = "Alert"
data_source = "GCP Cloud Audit"
platform = "GCP"
severity = "High"
priority = "High"
events:
$gcp.metadata.event_type = "USER_RESOURCE_UPDATE_PERMISSIONS"
$gcp.metadata.log_type = "GCP_CLOUDAUDIT"
$gcp.metadata.product_event_type = "SetIamPolicy"
$gcp.metadata.product_name = "Google Cloud Platform"
$gcp.metadata.vendor_name = "Google Cloud Platform"
$gcp.security_result.action = "ALLOW"
$gcp.target.resource.attribute.labels["ser_binding_deltas_action"] = "ADD"
$gcp.target.resource.attribute.labels["ser_binding_deltas_member"] = /.*@gmail\.com|.*@googlemail\.com|.*@googlegroups\.com/ nocase
outcome:
$risk_score = max(75)
$mitre_attack_tactic = "Persistence"
$mitre_attack_technique = "Create Account: Cloud Account"
$mitre_attack_technique_id = "T1136.003"
$event_count = count_distinct($gcp.metadata.id)
$network_http_user_agent = array_distinct($gcp.network.http.user_agent)
$principal_ip = array_distinct($gcp.principal.ip)
$principal_ip_country = array_distinct($gcp.principal.ip_geo_artifact.location.country_or_region)
$principal_ip_state = array_distinct($gcp.principal.ip_geo_artifact.location.state)
$principal_user_id = $gcp.principal.user.userid
$principal_user_display_name = $gcp.principal.user.user_display_name
$target_resource_name = $gcp.target.resource.name
$event_name = $gcp.metadata.product_event_type
$target_email_addresses = array_distinct($gcp.target.user.email_addresses)
condition:
$gcp
}
YARA-L Rule Structure
Condition
Fires when at least one $gcp event.
Events
$gcp
metadata.log_type = "GCP_CLOUDAUDIT"metadata.product_event_type = "SetIamPolicy"security_result.action = "ALLOW"target.resource.attribute.labels["ser_binding_deltas_action"] = "ADD"target.resource.attribute.labels["ser_binding_deltas_member"] matches ".*@gmail\.com|.*@googlemail\.com|.*@googlegroups\.com"
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(75) |
event_count | count_distinct($gcp.metadata.id) |
network_http_user_agent | array_distinct($gcp.network.http.user_agent) |
principal_ip | array_distinct($gcp.principal.ip) |
principal_ip_country | array_distinct($gcp.principal.ip_geo_artifact.location.country_or_region) |
principal_ip_state | array_distinct($gcp.principal.ip_geo_artifact.location.state) |
principal_user_id | $gcp.principal.user.userid |
principal_user_display_name | $gcp.principal.user.user_display_name |
target_resource_name | $gcp.target.resource.name |
event_name | $gcp.metadata.product_event_type |
target_email_addresses | array_distinct($gcp.target.user.email_addresses) |
GitHub Outgoing Organization Transfer Initiated
#Detects when a GitHub organization is transferred to another enterprise account. An adversary may attempt to transfer a GitHub organization to an enterprise account they control in order to steal data.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| GitHub | Audit log action org.transfer_outgoing: Organization transferred between enterprise accounts |
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_outgoing_organization_transfer_initiated {
meta:
author = "Google Cloud Security"
description = "Detects when a GitHub organization is transferred to another enterprise account. An adversary may attempt to transfer a GitHub organization to an enterprise account they control in order to steal data."
rule_id = "mr_052eb23f-84de-43a3-b9cb-736f8770cd8d"
rule_name = "GitHub Outgoing Organization Transfer Initiated"
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 = "Low"
priority = "Low"
platform = "GitHub"
data_source = "github"
mitre_attack_tactic = "Exfiltration"
mitre_attack_technique = "Transfer Data to Cloud Account"
mitre_attack_url = "https://attack.mitre.org/versions/v14/techniques/T1537/"
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 = "org.transfer_outgoing"
outcome:
$risk_score = max(35)
$mitre_attack_tactic = "Exfiltration"
$mitre_attack_technique = "Transfer Data to Cloud Account"
$mitre_attack_technique_id = "T1537"
$event_count = count_distinct($github.metadata.id)
$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 = "org.transfer_outgoing"
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($github.metadata.id) |
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) |
Okta User Logins From Multiple Cities
#Detects user logins for same user from different cities within 24 hours.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Privilege Escalation | |
| Stealth |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Okta | System Log event type user.session.start: User logged in to Okta |
Rules Detecting the Same Action
These rules filter on the same operation.
- Failed Logins from Unknown or Invalid User (Kusto)
- First Occurrence of Okta User Session Started via Proxy (Elastic)
- High-Risk Admin Activity (Kusto)
- Multiple Okta Sessions Detected for a Single User (Elastic)
- Multiple Okta User Authentication Events with Same Device Token Hash (Elastic)
- New Device/Location sign-in along with critical operation (Kusto)
- Okta AiTM Session Cookie Replay (Elastic)
- Okta Login From CrowdStrike Unmanaged Device (Panther)
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule okta_user_logins_from_multiple_cities {
meta:
author = "Google Cloud Security"
description = "Detects user logins for same user from different cities within 24 hours."
rule_id = "mr_b607de8a-7988-4f4f-8ecf-e8754de6bbae"
rule_name = "Okta User Logins From Multiple Cities"
reference = "https://help.okta.com/en-us/Content/Topics/Security/behavior-detection/logs-behavior-detection.htm"
mitre_attack_tactic = "Defense Evasion, Persistence, Privilege Escalation, Initial Access"
mitre_attack_technique = "Valid Accounts"
mitre_attack_url = "https://attack.mitre.org/techniques/T1078/"
mitre_attack_version = "v13.1"
type = "Alert"
data_source = "Okta"
severity = "Medium"
priority = "Medium"
events:
$login.metadata.product_name = "Okta"
$login.metadata.vendor_name = "Okta"
$login.metadata.event_type = "USER_LOGIN"
$login.metadata.product_event_type = "user.session.start"
$login.security_result.summary = "User login to Okta"
$login.principal.user.userid = $userid
$login.principal.location.city = $city
match:
$userid over 24h
outcome:
$risk_score = max(
35 +
// If the IP Address is marked as suspicious IP address by Okta ThreatInsight
if($login.security_result.detection_fields["threatSuspected"] = "true", 30) +
// Unauthorized target geographies
if($login.principal.ip_geo_artifact.location.country_or_region = "Cuba", 20) +
if($login.principal.ip_geo_artifact.location.country_or_region = "Iran", 20) +
if($login.principal.ip_geo_artifact.location.country_or_region = "North Korea", 20) +
if($login.principal.ip_geo_artifact.location.country_or_region = "Russia", 20) +
if($login.principal.ip_geo_artifact.location.country_or_region = "Syria", 20)
)
$mitre_attack_tactic = "Defense Evasion, Persistence, Privilege Escalation, Initial Access"
$mitre_attack_technique = "Valid Accounts"
$target_user_agent = array_distinct($login.network.http.user_agent)
$principal_ip = array_distinct($login.principal.ip)
$principal_ip_country = array_distinct($login.principal.ip_geo_artifact.location.country_or_region)
$principal_ip_state = array_distinct($login.principal.ip_geo_artifact.location.state)
$principal_ip_city = array_distinct($login.principal.location.city)
$dc_principal_ip_city = count_distinct($login.principal.location.city)
$security_result_summary = array_distinct($login.security_result.summary)
$principal_user_managers_email_addresses = array_distinct($login.principal.user.managers.email_addresses)
$principal_user_userid = array_distinct($login.principal.user.userid)
$target_user_email_addresses = array_distinct($login.target.user.email_addresses)
$target_user_userid = array_distinct($login.target.user.userid)
condition:
$login and $dc_principal_ip_city > 1
}
YARA-L Rule Structure
Condition
Fires when at least one $login event in the 24h window and $dc_principal_ip_city > 1.
Events
$login
metadata.product_event_type = "user.session.start"security_result.summary = "User login to Okta"
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 + if($login.security_result.detection_fields["threatSuspected"] = "true", 30) + if($login.principal.ip_geo_artifact.location.country_or_region = "Cuba", 20) + if($login.principal.ip_geo_artifact.location.country_or_region = "Iran", 20) + if($login.principal.ip_geo_artifact.location.country_or_region = "North Korea", 20) + if($login.principal.ip_geo_artifact.location.country_or_region = "Russia", 20) + if($login.principal.ip_geo_artifact.location.country_or_region = "Syria", 20) ) |
target_user_agent | array_distinct($login.network.http.user_agent) |
principal_ip | array_distinct($login.principal.ip) |
principal_ip_country | array_distinct($login.principal.ip_geo_artifact.location.country_or_region) |
principal_ip_state | array_distinct($login.principal.ip_geo_artifact.location.state) |
principal_ip_city | array_distinct($login.principal.location.city) |
dc_principal_ip_city | count_distinct($login.principal.location.city) |
security_result_summary | array_distinct($login.security_result.summary) |
principal_user_managers_email_addresses | array_distinct($login.principal.user.managers.email_addresses) |
principal_user_userid | array_distinct($login.principal.user.userid) |
target_user_email_addresses | array_distinct($login.target.user.email_addresses) |
target_user_userid | array_distinct($login.target.user.userid) |
VT Relationships File Contacts Domain
#Alert on known Hash querying a known hostname with VT Relationships
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 22: DNSEvent (DNS query) |
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule vt_relationships_file_contacts_domain {
meta:
author = "Google Cloud Security"
description = "Alert on known Hash querying a known hostname with VT Relationships"
rule_id = "mr_42ee37ba-cb36-49f2-b784-05ca0bd55b9f"
rule_name = "VT Relationships File Contacts Domain"
type = "alert"
tags = "vt"
data_source = "microsoft sysmon, crowdstrike"
severity = "High"
priority = "High"
events:
$dns.metadata.event_type = "NETWORK_DNS"
$dns.principal.process.file.sha256 = $file_hash
$dns.network.dns.questions.name = $domain
strings.coalesce($dns.principal.hostname, $dns.target.user.userid) = $hostname_or_userid
// Uses VirusTotal integration via entity graph, this section maps to principal process file hash
$vt.graph.metadata.entity_type = "FILE"
$vt.graph.metadata.source_type = "GLOBAL_CONTEXT"
$vt.graph.metadata.vendor_name = "VirusTotal"
$vt.graph.metadata.product_name = "VirusTotal Relationships"
$vt.graph.entity.file.sha256 = $file_hash
// Uses VirusTotal integration via entity graph, this section maps the hash to the domain it contacts
$vt.graph.relations.entity_type = "DOMAIN_NAME"
$vt.graph.relations.relationship = "CONTACTS"
$vt.graph.relations.entity.domain.name = $domain
match:
$hostname_or_userid over 5m
outcome:
$risk_score = 85
$event_count = count_distinct($dns.metadata.id)
$network_dns_questions_name = array_distinct($dns.network.dns.questions.name)
$network_dns_answers_data = array_distinct($dns.network.dns.answers.data)
// added to populate alert graph with additional context
$principal_ip = array_distinct($dns.principal.ip)
$target_ip = array_distinct($dns.target.ip)
$principal_process_pid = array_distinct($dns.principal.process.pid)
$principal_process_file_full_path = array_distinct($dns.principal.process.file.full_path)
$principal_process_product_specific_process_id = array_distinct($dns.principal.process.product_specific_process_id)
$principal_user_userid = array_distinct($dns.principal.user.userid)
$principal_process_command_line = array_distinct($dns.principal.process.command_line)
$principal_process_file_sha256 = array_distinct($dns.principal.process.file.sha256)
$principal_process_parent_process_product_specific_process_id = array_distinct($dns.principal.process.parent_process.product_specific_process_id)
condition:
$dns and $vt
}
YARA-L Rule Structure
Condition
Fires when at least one $dns event in the 5m window and $vt matches entity context.
Events
$dns
$vt
graph.metadata.entity_type = "FILE"graph.metadata.source_type = "GLOBAL_CONTEXT"graph.metadata.vendor_name = "VirusTotal"graph.metadata.product_name = "VirusTotal Relationships"graph.relations.entity_type = "DOMAIN_NAME"graph.relations.relationship = "CONTACTS"
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 | 85 |
event_count | count_distinct($dns.metadata.id) |
network_dns_questions_name | array_distinct($dns.network.dns.questions.name) |
network_dns_answers_data | array_distinct($dns.network.dns.answers.data) |
principal_ip | array_distinct($dns.principal.ip) |
target_ip | array_distinct($dns.target.ip) |
principal_process_pid | array_distinct($dns.principal.process.pid) |
principal_process_file_full_path | array_distinct($dns.principal.process.file.full_path) |
principal_process_product_specific_process_id | array_distinct($dns.principal.process.product_specific_process_id) |
principal_user_userid | array_distinct($dns.principal.user.userid) |
principal_process_command_line | array_distinct($dns.principal.process.command_line) |
principal_process_file_sha256 | array_distinct($dns.principal.process.file.sha256) |
principal_process_parent_process_product_specific_process_id | array_distinct($dns.principal.process.parent_process.product_specific_process_id) |