Detection rules › YARA-L
YARA-L rules: 23
| Rule | Severity |
|---|---|
| Entra ID application enumeration observed in the Microsoft Graph API | low |
| File Download Using Notepad++ GUP Utility | high |
| Google Workspace Admin Role Assignment | high |
| ShimCache Flush | high |
Entra ID application enumeration observed in the Microsoft Graph API
#Identify the use GraphRunner's Invoke-DumpApps function to collect applications and external apps as well as users that consented to permissions in their apps.
References
Rule Body
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule ms_graph_enumerate_applications {
meta:
author = "Google Cloud Security"
description = "Identify the use GraphRunner's Invoke-DumpApps function to collect applications and external apps as well as users that consented to permissions in their apps."
rule_id = "mr_3715d986-b1a9-4bb2-b625-acc54cd5322d"
rule_name = "Entra ID application enumeration observed in the Microsoft Graph API"
assumption = "While additional API endpoints are called in the function, this rule is stripped down to the applications and servicePrincipals endpoint recursively requesting the appRole assigned to."
reference = "https://github.com/dafthack/GraphRunner/blob/main/GraphRunner.ps1"
type = "Alert"
data_source = "MS Graph Activity Logs"
platform = "Azure"
severity = "Low"
priority = "Low"
events:
$app_api.metadata.event_type = "NETWORK_HTTP"
$app_api.metadata.product_event_type = "Microsoft Graph Activity"
$app_api.target.url = "https://graph.microsoft.com/v1.0/applications" nocase
$app_api.network.http.method = "GET"
$app_api.network.http.response_code = 200
//Can be tuned for specific User Agent strings being used - by default GraphRunner does not forge the UA for this action
//$app_api.network.http.user_agent = /PowerShell/ nocase
$app_api.principal.ip = $ip
$app_api.network.session_id = $session
$app_api.metadata.event_timestamp.seconds < $role_api.metadata.event_timestamp.seconds
$role_api.metadata.event_type = "NETWORK_HTTP"
$role_api.metadata.product_event_type = "Microsoft Graph Activity"
re.regex($role_api.target.url, `^https:\/\/graph.microsoft.com\/v1.0\/servicePrincipals\(appId='.*'\)/appRoleAssignedTo$`) nocase
$role_api.network.http.method = "GET"
$role_api.network.http.response_code = 200
//Can be tuned for specific User Agent strings being used - by default GraphRunner does not forge the UA for this action
//$role_api.network.http.user_agent = /PowerShell/ nocase
$role_api.principal.ip = $ip
$role_api.network.session_id = $session
re.capture($role_api.target.url, `^https:\/\/graph.microsoft.com\/v1.0\/servicePrincipals\(appId='(.*)'\)/appRoleAssignedTo$`) = $app_guid
match:
$ip, $session over 5m
outcome:
$risk_score = 35
$event_count = count_distinct($app_api.metadata.id) + count_distinct($role_api.metadata.id)
$requesting_user_guid = array_distinct($app_api.principal.user.userid)
$requesting_ip = array_distinct($app_api.principal.ip)
$user_agent = array_distinct($app_api.network.http.user_agent)
$location = array_distinct($app_api.principal.location.name)
$target_application_id_guid = array_distinct($app_api.target.resource.product_object_id)
$session_id = array_distinct($app_api.network.session_id)
$application_count = count_distinct($app_guid)
$application_list = array_distinct($app_guid) //limited to 25 applications
condition:
$app_api and $role_api
}
YARA-L Rule Structure
Condition
Fires when at least one $app_api event in the 5m window and at least one $role_api event in the 5m window.
Events
$app_api
metadata.product_event_type = "Microsoft Graph Activity"target.url = "https://graph.microsoft.com/v1.0/applications"network.http.method = "GET"network.http.response_code = "200"
$role_api
metadata.product_event_type = "Microsoft Graph Activity"target.url matches "^https:\/\/graph.microsoft.com\/v1.0\/servicePrincipals\(appId='.*'\)/appRoleAssignedTo$"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($app_api.metadata.id) + count_distinct($role_api.metadata.id) |
requesting_user_guid | array_distinct($app_api.principal.user.userid) |
requesting_ip | array_distinct($app_api.principal.ip) |
user_agent | array_distinct($app_api.network.http.user_agent) |
location | array_distinct($app_api.principal.location.name) |
target_application_id_guid | array_distinct($app_api.target.resource.product_object_id) |
session_id | array_distinct($app_api.network.session_id) |
application_count | count_distinct($app_guid) |
application_list | array_distinct($app_guid) |
File Download Using Notepad++ GUP Utility
#Detects execution of the Notepad++ updater (gup) from a process other than Notepad++ to download files
Known False Positives
- Other parent processes other than notepad++ using GUP that are not currently identified
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
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 file_download_using_notepad_plus_plus_gup_utility {
meta:
author = "Nasreddine Bencherchali (Nextron Systems)"
description = "Detects execution of the Notepad++ updater (gup) from a process other than Notepad++ to download files"
reference = "https://github.com/SigmaHQ/sigma/blob/master/rules/windows/process_creation/proc_creation_win_gup_download.yml"
license = "https://github.com/SigmaHQ/Detection-Rule-License/blob/main/LICENSE.Detection.Rules.md"
rule_name = "File Download Using Notepad++ GUP Utility"
sigma_uuid = "44143844-0631-49ab-97a0-96387d6b2d7c"
sigma_status = "test"
rule_id = "mr_0e2db84a-0176-460c-b0b6-572e17842acd"
tactic = "TA0011"
technique = "T1105"
type = "Detection"
data_source = "Sysmon"
platform = "Windows"
severity = "High"
priority = "High"
false_positives = "Other parent processes other than notepad++ using GUP that are not currently identified"
events:
$process.metadata.event_type = "PROCESS_LAUNCH"
(
re.regex($process.target.process.file.full_path, `\\GUP\.exe$`) nocase or
$process.src.process.file.full_path = "gup.exe" nocase
)
(
strings.contains(strings.to_lower($process.target.process.command_line), " -unzipto ") and
strings.contains(strings.to_lower($process.target.process.command_line), "http")
)
not re.regex($process.principal.process.file.full_path, `\\notepad\+\+\.exe$`) nocase
$process.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
//example usage of specifying test user and hostname to adjust risk score
$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 "\\GUP\.exe$"src.process.file.full_path = "gup.exe"target.process.command_line contains " -unzipto "target.process.command_line contains "http"principal.process.file.full_path matches "\\notepad\+\+\.exe$"
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)) |
Google Workspace Admin Role Assignment
#Identifies when an administrator role is assigned to a user account in Google Workspace. Security teams can monitor for the malicious or accidental assignment of administrator privileges to prevent unauthorized access to data.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Google Workspace | ASSIGN_ROLE: Assign Role |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
// Copyright 2023 Google LLC. Licensed under Apache-2.0.
rule google_workspace_admin_role_assignment {
meta:
author = "Google Cloud Security"
description = "Identifies when an administrator role is assigned to a user account in Google Workspace. Security teams can monitor for the malicious or accidental assignment of administrator privileges to prevent unauthorized access to data."
rule_id = "mr_0c547820-1095-42a1-94d8-a00d5ef2df76"
rule_name = "Google Workspace Admin Role Assignment"
mitre_attack_tactic = "Persistence"
mitre_attack_technique = "Account Manipulation: Additional Cloud Roles"
mitre_attack_url = "https://attack.mitre.org/techniques/T1098/003/"
mitre_attack_version = "v13.1"
type = "Alert"
data_source = "Workspace Activity"
severity = "High"
priority = "High"
events:
$ws.metadata.vendor_name = "Google Workspace"
$ws.metadata.product_name = "admin"
$ws.metadata.event_type = "USER_RESOURCE_UPDATE_PERMISSIONS"
$ws.metadata.product_event_type = "ASSIGN_ROLE"
$ws.security_result.category_details = "DELEGATED_ADMIN_SETTINGS"
outcome:
$risk_score = max(35 +
if($ws.target.resource.attribute.roles.name = "_SEED_ADMIN_ROLE", 50)
)
$mitre_attack_tactic = "Persistence"
$mitre_attack_technique = "Account Manipulation: Additional Cloud Roles"
$mitre_attack_technique_id = "T1098.003"
$event_count = count_distinct($ws.metadata.id)
$principal_ip = array_distinct($ws.principal.ip)
$principal_country = array_distinct($ws.principal.ip_geo_artifact.location.country_or_region)
$principal_state = array_distinct($ws.principal.ip_geo_artifact.location.state)
$principal_user_emails = array_distinct($ws.principal.user.email_addresses)
$target_user_emails = array_distinct($ws.target.user.email_addresses)
$principal_user_id = $ws.principal.user.userid
$role_name = array_distinct($ws.target.resource.attribute.roles.name)
condition:
$ws
}
YARA-L Rule Structure
Condition
Fires when at least one $ws event.
Events
$ws
metadata.product_event_type = "ASSIGN_ROLE"security_result.category_details = "DELEGATED_ADMIN_SETTINGS"
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($ws.target.resource.attribute.roles.name = "_SEED_ADMIN_ROLE", 50) ) |
event_count | count_distinct($ws.metadata.id) |
principal_ip | array_distinct($ws.principal.ip) |
principal_country | array_distinct($ws.principal.ip_geo_artifact.location.country_or_region) |
principal_state | array_distinct($ws.principal.ip_geo_artifact.location.state) |
principal_user_emails | array_distinct($ws.principal.user.email_addresses) |
target_user_emails | array_distinct($ws.target.user.email_addresses) |
principal_user_id | $ws.principal.user.userid |
role_name | array_distinct($ws.target.resource.attribute.roles.name) |
ShimCache Flush
#Detects actions that clear the local ShimCache and remove forensic evidence
Known False Positives
- Unknown
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Defense Impairment |
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 shimcache_flush {
meta:
author = "Florian Roth (Nextron Systems)"
description = "Detects actions that clear the local ShimCache and remove forensic evidence"
reference = "https://github.com/SigmaHQ/sigma/blob/master/rules/windows/process_creation/proc_creation_win_rundll32_susp_shimcache_flush.yml"
license = "https://github.com/SigmaHQ/Detection-Rule-License/blob/main/LICENSE.Detection.Rules.md"
rule_name = "ShimCache Flush"
rule_id = "mr_5f6699e1-0518-4664-85aa-9a716f0d4dd6"
sigma_uuid = "b0524451-19af-4efa-a46f-562a977f792e"
sigma_status = "stable"
tactic = "TA0005"
//tactic = "TA0008"
//technique = "T1021.001"
technique = "T1112"
type = "Detection"
data_source = "Sysmon"
platform = "Windows"
severity = "High"
priority = "High"
false_positives = "Unknown"
events:
$process.metadata.event_type = "PROCESS_LAUNCH"
(
(
strings.contains(strings.to_lower($process.target.process.command_line), "rundll32") and
strings.contains(strings.to_lower($process.target.process.command_line), "apphelp.dll")
)
and
(
strings.contains(strings.to_lower($process.target.process.command_line), "shimflushcache") or
strings.contains($process.target.process.command_line, "#250")
)
)
or
(
(
strings.contains(strings.to_lower($process.target.process.command_line), "rundll32") and
strings.contains(strings.to_lower($process.target.process.command_line), "kernel32.dll")
)
and
(
strings.contains(strings.to_lower($process.target.process.command_line), "baseflushappcompatcache") or
strings.contains($process.target.process.command_line, "#46")
)
)
$process.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
//example usage of specifying test user and hostname to adjust risk score
$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.command_line contains "rundll32"target.process.command_line contains "apphelp.dll"target.process.command_line contains "shimflushcache"target.process.command_line contains "#250"target.process.command_line contains "rundll32"target.process.command_line contains "kernel32.dll"target.process.command_line contains "baseflushappcompatcache"target.process.command_line contains "#46"
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)) |