Detection rules › YARA-L
YARA-L rules: 38
Hunt for Bad Request errors against the Groups endpoint in the Microsoft Graph API
#Identify a 400 bad request to the groups endpoint in the MS Graph API. GraphRunner's Invoke-SecurityGroupCloner function will trigger this if it fails to duplicate a group.
References
Rule Body
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule ms_graph_group_bad_request {
meta:
author = "Google Cloud Security"
description = "Identify a 400 bad request to the groups endpoint in the MS Graph API. GraphRunner's Invoke-SecurityGroupCloner function will trigger this if it fails to duplicate a group."
rule_id = "mr_09bafa2b-5131-452c-bf5a-021063cfd5c2"
rule_name = "Hunt for Bad Request errors against the Groups endpoint in the Microsoft Graph API"
assumption = "This detects all web activity associated with the groups endpoint with a response code 400, normal admin actions could trigger as well. Tune using IP address ranges for admins and user agents."
reference = "https://github.com/dafthack/GraphRunner/blob/main/GraphRunner.ps1"
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
$api.network.http.method = "POST"
$api.network.http.response_code = 400
//Can be tuned for specific User Agent strings being used
//$api.network.http.user_agent = /PowerShell/ nocase
//SkypeSpaces triggered during testing so filtering this out
$api.network.http.user_agent != "SkypeSpaces/1.0a$*+"
$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 = "POST"network.http.response_code = "400"network.http.user_agent != "SkypeSpaces/1.0a$*+"
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) |
Hunt for the delete method in Microsoft Graph API calls
#Identify when the http delete method is being called within the Microsoft Graph API.
Rule Body
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule ms_graph_delete_method {
meta:
author = "Google Cloud Security"
description = "Identify when the http delete method is being called within the Microsoft Graph API."
rule_id = "mr_9e5f6adc-84f9-46f3-bf9e-abe38e14a77b"
rule_name = "Hunt for the delete method in Microsoft Graph API calls"
assumption = "Tuning is needed to identify administrative activities as general maintenance that may be logged"
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.network.http.method = "DELETE"
//Can be tuned for specific APIs being used in delete operations if desired
//$api.target.url = /https:\/\/graph.microsoft.com\/v1.0\/groups\/.*\/members/ nocase
//Can be tuned for specific User Agent strings being used
//$api.network.http.user_agent = /PowerShell/ nocase
//Can be tuned to eliminate Azure IP ranges or other IP addresses
NOT net.ip_in_range_cidr($api.principal.ip, "20.151.0.0/16")
$api.principal.ip = $ip
$api.network.session_id = $session
match:
$ip, $session over 5m
outcome:
$risk_score = 15
$event_count = count($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"network.http.method = "DELETE"principal.ip cidr_match "20.151.0.0/16"
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 | 15 |
event_count | count($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) |
O365 Entra ID App Client Secret Added, Updated or Deleted
#Secrets added to applications have legitimate purposes, but can also be a method of persistence. This alert will trigger on creation, modification or delete of a client secret
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | Update application – Certificates and secrets management |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule o365_entra_id_client_secret_add_update_delete_in_app {
meta:
author = "Google Cloud Security"
description = "Secrets added to applications have legitimate purposes, but can also be a method of persistence. This alert will trigger on creation, modification or delete of a client secret"
rule_id = "mr_24ac7907-9d86-4089-901c-61f8ff1e7730"
rule_name = "O365 Entra ID App Client Secret Added, Updated or Deleted"
tactic = "TA0003"
technique = "T1098.001"
type = "alert"
platform = "azure"
data_source = "o365"
severity = "Medium"
priority = "Medium"
events:
$app.metadata.event_type = "USER_RESOURCE_UPDATE_CONTENT"
$app.metadata.product_name = "Office 365"
$app.metadata.product_event_type = /Update application.*Certificates and secrets management/
$app.metadata.vendor_name = "Microsoft"
$app.security_result.action = "ALLOW"
$app.principal.user.userid = $userid
match:
$userid over 5m
outcome:
$risk_score = 65
$event_count = count_distinct($app.metadata.id)
$security_summary = array_distinct($app.security_result.summary)
$user_agent = array_distinct($app.network.http.user_agent)
$target_entra_id_application = array_distinct(re.capture($app.network.http.user_agent,`\"AppId\":\"(.*)`))
//added to populate alert graph with additional context
$principal_user_userid = array_distinct($app.principal.user.userid)
condition:
$app
}
YARA-L Rule Structure
Condition
Fires when at least one $app event in the 5m window.
Events
$app
metadata.product_event_type matches "Update application.*Certificates and secrets management"security_result.action = "ALLOW"
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 | 65 |
event_count | count_distinct($app.metadata.id) |
security_summary | array_distinct($app.security_result.summary) |
user_agent | array_distinct($app.network.http.user_agent) |
target_entra_id_application | array_distinct(re.capture($app.network.http.user_agent,`\"AppId\":\"(.*)`)) |
principal_user_userid | array_distinct($app.principal.user.userid) |
Okta ThreatInsight Targeted Bruteforce Attack
#Okta ThreatInsight detects access requests from known malicious IPs targeting a specific org.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Okta | System Log event type security.attack.start: Org under attack per Threat Insight |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule okta_threatinsight_targeted_brute_force_attack {
meta:
author = "Google Cloud Security"
description = "Okta ThreatInsight detects access requests from known malicious IPs targeting a specific org."
rule_id = "mr_ce576f12-ee6d-4282-9f7b-f955af183eb7"
rule_name = "Okta ThreatInsight Targeted Bruteforce Attack"
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"
mitre_attack_url = "https://attack.mitre.org/techniques/T1110/"
mitre_attack_version = "v13.1"
type = "Alert"
data_source = "Okta"
severity = "Medium"
priority = "Medium"
events:
$bruteforce.metadata.product_name = "Okta"
$bruteforce.metadata.vendor_name = "Okta"
$bruteforce.metadata.event_type = "USER_UNCATEGORIZED"
$bruteforce.metadata.product_event_type = "security.attack.start"
$bruteforce.principal.user.userid = $userid
match:
$userid over 1h
outcome:
$risk_score = max(35)
$mitre_attack_tactic = "Credential Access"
$mitre_attack_technique = "Brute Force"
$principal_user_userid = array_distinct($bruteforce.principal.user.userid)
$principal_user_email_addresses = array_distinct($bruteforce.principal.user.email_addresses)
$security_result_summary = array_distinct($bruteforce.security_result.summary)
$security_result_severity = array_distinct($bruteforce.security_result.severity)
condition:
$bruteforce
}
YARA-L Rule Structure
Condition
Fires when at least one $bruteforce event in the 1h window.
Events
$bruteforce
metadata.product_event_type = "security.attack.start"
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) |
principal_user_userid | array_distinct($bruteforce.principal.user.userid) |
principal_user_email_addresses | array_distinct($bruteforce.principal.user.email_addresses) |
security_result_summary | array_distinct($bruteforce.security_result.summary) |
security_result_severity | array_distinct($bruteforce.security_result.severity) |