Detection rules › YARA-L
YARA-L rules: 1w
Hunt for Groups endpoint requests in the Microsoft Graph API
#This detects when the groups endpoint is called in the Graph API. GraphRunner contains a function called Get-DynamicGroups but other tools may as well.
References
Rule Body
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule ms_graph_groups_endpoint_requests {
meta:
author = "Google Cloud Security"
description = "This detects when the groups endpoint is called in the Graph API. GraphRunner contains a function called Get-DynamicGroups but other tools may as well."
rule_id = "mr_cb4ec70d-811b-477c-9b0c-c50e54058f2d"
rule_name = "Hunt for Groups endpoint requests in the Microsoft Graph API"
reference = "https://github.com/dafthack/GraphRunner/blob/main/GraphRunner.ps1"
assumption = "Applications that perform this action will need to be filtered and thresholding in the condition could be added for additional tuning as needed"
type = "hunt"
data_source = "MS Graph Activity Logs"
platform = "Azure"
severity = "Low"
priority = "Low"
events:
$api.metadata.event_type = "NETWORK_HTTP"
$api.metadata.product_event_type = "Microsoft Graph Activity"
$api.target.url = "https://graph.microsoft.com/v1.0/groups" nocase
//Can be tuned for specific User Agent strings being used - by default GraphRunner does not forge the UA for this action
//$api.network.http.user_agent = /PowerShell/ nocase
$api.network.http.method = "GET"
$api.network.http.response_code = 200
$api.principal.ip = $ip
$api.network.session_id = $session
match:
$ip, $session over 5m
outcome:
$risk_score = 35
$event_count = count_distinct($api.metadata.id)
$requesting_user_guid = array_distinct($api.principal.user.userid)
$requesting_ip = array_distinct($api.principal.ip)
$user_agent = array_distinct($api.network.http.user_agent)
$location = array_distinct($api.principal.location.name)
$target_application_id_guid = array_distinct($api.target.resource.product_object_id)
$session_id = array_distinct($api.network.session_id)
$target_url = array_distinct($api.target.url)
condition:
$api
}
YARA-L Rule Structure
Condition
Fires when at least one $api event in the 5m window.
Events
$api
metadata.product_event_type = "Microsoft Graph Activity"target.url = "https://graph.microsoft.com/v1.0/groups"network.http.method = "GET"network.http.response_code = "200"
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 | 35 |
event_count | count_distinct($api.metadata.id) |
requesting_user_guid | array_distinct($api.principal.user.userid) |
requesting_ip | array_distinct($api.principal.ip) |
user_agent | array_distinct($api.network.http.user_agent) |
location | array_distinct($api.principal.location.name) |
target_application_id_guid | array_distinct($api.target.resource.product_object_id) |
session_id | array_distinct($api.network.session_id) |
target_url | array_distinct($api.target.url) |
Okta ThreatInsight Login Failure With High Unknown Users
#Okta's ThreatInsight can identify multiple login failures with high unknown users count from the same IP across one or more Okta orgs.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Okta | System Log event type security.threat.detected: Request from malicious IP detected by ThreatInsight |
Rules Detecting the Same Action
These rules filter on the same operation.
- Device Registration from Malicious IP (Kusto)
- Okta Security Threat Detected (Sigma)
- Okta ThreatInsight Security Threat Detected (Panther)
- Okta ThreatInsight Suspected Bruteforce Attack (YARA-L)
- Okta ThreatInsight Suspected Password Spray Attack (YARA-L)
- Okta ThreatInsight Threat Detected (Splunk)
- Okta ThreatInsight Threat Suspected Promotion (Elastic)
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule okta_threatinsight_login_failure_with_high_unknown_users {
meta:
author = "Google Cloud Security"
description = "Okta's ThreatInsight can identify multiple login failures with high unknown users count from the same IP across one or more Okta orgs."
rule_id = "mr_41bafd12-7b1d-4b73-adf6-621c0431b538"
rule_name = "Okta ThreatInsight Login Failure With High Unknown Users"
reference = "https://help.okta.com/en-us/Content/Topics/Security/threat-insight/configure-threatinsight-system-log.htm"
mitre_attack_tactic = "Credential Access"
mitre_attack_technique = "Brute Force: Credential Stuffing"
mitre_attack_url = "https://attack.mitre.org/techniques/T1110/004/"
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_UNCATEGORIZED"
$login.metadata.product_event_type = "security.threat.detected"
$login.security_result.category_details = /Login failures with high unknown users count/
$login.principal.user.userid = $userid
$login.principal.ip = $ip
match:
$userid, $ip over 1h
outcome:
$risk_score = max(
// Baseline
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 = "Credential Access"
$mitre_attack_technique = "Brute Force: Credential Stuffing"
$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)
$security_result_summary = array_distinct($login.security_result.summary)
condition:
$login
}
YARA-L Rule Structure
Condition
Fires when at least one $login event in the 1h window.
Events
$login
metadata.product_event_type = "security.threat.detected"security_result.category_details matches "Login failures with high unknown users count"
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) |
security_result_summary | array_distinct($login.security_result.summary) |
RestrictedAdminMode Registry Value Tampering
#Detects changes to the "DisableRestrictedAdmin" registry value in order to disable or enable RestrictedAdmin mode.\nRestrictedAdmin mode prevents the transmission of reusable credentials to the remote system to which you connect using Remote Desktop.\nThis prevents your credentials from being harvested during the initial connection process if the remote server has been compromise
Known False Positives
- Unknown
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Defense Impairment |
References
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 13: RegistryEvent (Value Set) |
| Security-Auditing | Event ID 4657: A registry value was modified. |
Rule Body
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule restrictedadminmode_registry_value_tampering {
meta:
author = "frack113"
description = "Detects changes to the \"DisableRestrictedAdmin\" registry value in order to disable or enable RestrictedAdmin mode.\nRestrictedAdmin mode prevents the transmission of reusable credentials to the remote system to which you connect using Remote Desktop.\nThis prevents your credentials from being harvested during the initial connection process if the remote server has been compromise"
reference = "https://github.com/SigmaHQ/sigma/blob/master/rules/windows/registry/registry_set/registry_set_lsa_disablerestrictedadmin.yml"
license = "https://github.com/SigmaHQ/Detection-Rule-License/blob/main/LICENSE.Detection.Rules.md"
rule_name = "RestrictedAdminMode Registry Value Tampering"
rule_id = "mr_c996dba7-ebc0-4472-849c-d6ddebffa601"
sigma_uuid = "d6ce7ebd-260b-4323-9768-a9631c8d4db2"
sigma_status = "test"
tactic = "TA0005"
technique = "T1112"
type = "Detection"
data_source = "Sysmon Windows Registry"
platform = "Windows"
severity = "High"
priority = "High"
false_positives = "Unknown"
events:
$reg.metadata.event_type = "REGISTRY_MODIFICATION"
re.regex($reg.target.registry.registry_key, `System\\CurrentControlSet\\Control\\Lsa\\DisableRestrictedAdmin$`) nocase
$reg.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
//example usage of specifying test user and hostname to adjust risk score
$risk_score = max(if($reg.principal.user.userid = "user" and $reg.principal.hostname = "hostname", 0, 15))
$principal_hostname = array_distinct($reg.principal.hostname)
$principal_process_pid = array_distinct($reg.principal.process.pid)
$principal_process_file_full_path = array_distinct($reg.principal.process.file.full_path)
$principal_process_product_specific_process_id = array_distinct($reg.principal.process.product_specific_process_id)
$principal_user_userid = array_distinct($reg.principal.user.userid)
$target_registry_key = array_distinct($reg.target.registry.registry_key)
$target_registry_value_data = array_distinct($reg.target.registry.registry_value_data)
$log_type = array_distinct(strings.concat($reg.metadata.log_type,"/",$reg.metadata.product_event_type))
condition:
$reg
}
YARA-L Rule Structure
Condition
Fires when at least one $reg event in the 5m window.
Events
$reg
target.registry.registry_key matches "System\\CurrentControlSet\\Control\\Lsa\\DisableRestrictedAdmin$"
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($reg.principal.user.userid = "user" and $reg.principal.hostname = "hostname", 0, 15)) |
principal_hostname | array_distinct($reg.principal.hostname) |
principal_process_pid | array_distinct($reg.principal.process.pid) |
principal_process_file_full_path | array_distinct($reg.principal.process.file.full_path) |
principal_process_product_specific_process_id | array_distinct($reg.principal.process.product_specific_process_id) |
principal_user_userid | array_distinct($reg.principal.user.userid) |
target_registry_key | array_distinct($reg.target.registry.registry_key) |
target_registry_value_data | array_distinct($reg.target.registry.registry_value_data) |
log_type | array_distinct(strings.concat($reg.metadata.log_type,"/",$reg.metadata.product_event_type)) |
Suspicious Download Via Certutil.EXE
#Detects the execution of certutil with certain flags that allow the utility to download files
Known False Positives
- Unknown
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
References
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 2025 Google LLC. Licensed under Apache-2.0.
rule suspicious_download_via_certutil_exe {
meta:
author = "Florian Roth (Nextron Systems), Jonhnathan Ribeiro, oscd.community, Nasreddine Bencherchali (Nextron Systems)"
description = "Detects the execution of certutil with certain flags that allow the utility to download files"
reference = "https://github.com/SigmaHQ/sigma/blob/master/rules/windows/process_creation/proc_creation_win_certutil_download.yml"
license = "https://github.com/SigmaHQ/Detection-Rule-License/blob/main/LICENSE.Detection.Rules.md"
rule_name = "Suspicious Download Via Certutil.EXE"
sigma_uuid = "19b08b1c-861d-4e75-a1ef-ea0c1baf202b"
sigma_status = "test"
le_id = "mr_e9ec6964-4883-47b8-a6b3-2ece9962a813"
tactic = "TA0005"
technique = "T1027"
type = "Detection"
data_source = "Sysmon"
platform = "Windows"
severity = "Medium"
priority = "Medium"
false_positives = "Unknown"
events:
$process.metadata.event_type = "PROCESS_LAUNCH"
(
re.regex($process.target.process.file.full_path, `\\certutil\.exe$`) nocase or
$process.src.process.file.full_path = "certutil.exe" nocase
)
(
strings.contains(strings.to_lower($process.target.process.command_line), "urlcache ") or
strings.contains(strings.to_lower($process.target.process.command_line), "verifyctl ")
)
strings.contains(strings.to_lower($process.target.process.command_line), "http")
$process.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
$risk_score = max(if($process.principal.user.userid = "user" and $process.principal.hostname = "hostname", 0, 15))
$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_specfic_process_id = array_distinct($process.principal.process.product_specific_process_id)
$principal_process_parent_process_product_specfic_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_specfic_process_id = array_distinct($process.target.process.product_specific_process_id)
$principal_user_userid = array_distinct($process.principal.user.userid)
$log_type = array_distinct(strings.concat($process.metadata.log_type,"/",$process.metadata.product_event_type))
condition:
$process
}
YARA-L Rule Structure
Condition
Fires when at least one $process event in the 5m window.
Events
$process
target.process.file.full_path matches "\\certutil\.exe$"src.process.file.full_path = "certutil.exe"target.process.command_line contains "urlcache "target.process.command_line contains "verifyctl "target.process.command_line contains "http"
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($process.principal.user.userid = "user" and $process.principal.hostname = "hostname", 0, 15)) |
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_specfic_process_id | array_distinct($process.principal.process.product_specific_process_id) |
principal_process_parent_process_product_specfic_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_specfic_process_id | array_distinct($process.target.process.product_specific_process_id) |
principal_user_userid | array_distinct($process.principal.user.userid) |
log_type | array_distinct(strings.concat($process.metadata.log_type,"/",$process.metadata.product_event_type)) |