Detection rules › YARA-L

YARA-L rules: 23

Entra ID application enumeration observed in the Microsoft Graph API

#
Severity
low
Type
Alert
Time window
5m
Match by
ip, session
Author
Google Cloud Security
Source
github.com/chronicle/detection-rules

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 NETWORK_HTTP

  • 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 NETWORK_HTTP

  • 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

Order
$app_api before $role_api
Match key
$ip (principal.ip), $session (network.session_id)
Within
5m

Outcome

Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.

FieldExpression
risk_score35
event_countcount_distinct($app_api.metadata.id) + count_distinct($role_api.metadata.id)
requesting_user_guidarray_distinct($app_api.principal.user.userid)
requesting_iparray_distinct($app_api.principal.ip)
user_agentarray_distinct($app_api.network.http.user_agent)
locationarray_distinct($app_api.principal.location.name)
target_application_id_guidarray_distinct($app_api.target.resource.product_object_id)
session_idarray_distinct($app_api.network.session_id)
application_countcount_distinct($app_guid)
application_listarray_distinct($app_guid)

File Download Using Notepad++ GUP Utility

#
Severity
high
Type
Detection
Time window
5m
Match by
hostname
Author
Nasreddine Bencherchali (Nextron Systems)
Source
github.com/chronicle/detection-rules

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

TacticTechniques
Command & Control

References

Telemetry Coverage

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 PROCESS_LAUNCH (1, 4688)

  • 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

Match key
$hostname (principal.hostname)
Within
5m

Outcome

Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.

FieldExpression
risk_scoremax(if($process.principal.user.userid = "user" and $process.principal.hostname = "hostname", 0, 15))
principal_process_pidarray_distinct($process.principal.process.pid)
principal_process_command_linearray_distinct($process.principal.process.command_line)
principal_process_file_sha256array_distinct($process.principal.process.file.sha256)
principal_process_file_full_patharray_distinct($process.principal.process.file.full_path)
principal_process_product_specfic_process_idarray_distinct($process.principal.process.product_specific_process_id)
principal_process_parent_process_product_specfic_process_idarray_distinct($process.principal.process.parent_process.product_specific_process_id)
target_process_pidarray_distinct($process.target.process.pid)
target_process_command_linearray_distinct($process.target.process.command_line)
target_process_file_sha256array_distinct($process.target.process.file.sha256)
target_process_file_full_patharray_distinct($process.target.process.file.full_path)
target_process_product_specfic_process_idarray_distinct($process.target.process.product_specific_process_id)
principal_user_useridarray_distinct($process.principal.user.userid)
log_typearray_distinct(strings.concat($process.metadata.log_type,"/",$process.metadata.product_event_type))

Google Workspace Admin Role Assignment

#
Severity
high
Type
Alert
Author
Google Cloud Security
Source
github.com/chronicle/detection-rules

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

Telemetry Coverage

PlatformRecord / event type
Google WorkspaceASSIGN_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 USER_RESOURCE_UPDATE_PERMISSIONS

  • 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.

FieldExpression
risk_scoremax(35 + if($ws.target.resource.attribute.roles.name = "_SEED_ADMIN_ROLE", 50) )
event_countcount_distinct($ws.metadata.id)
principal_iparray_distinct($ws.principal.ip)
principal_countryarray_distinct($ws.principal.ip_geo_artifact.location.country_or_region)
principal_statearray_distinct($ws.principal.ip_geo_artifact.location.state)
principal_user_emailsarray_distinct($ws.principal.user.email_addresses)
target_user_emailsarray_distinct($ws.target.user.email_addresses)
principal_user_id$ws.principal.user.userid
role_namearray_distinct($ws.target.resource.attribute.roles.name)

ShimCache Flush

#
Severity
high
Type
Detection
Time window
5m
Match by
hostname
Author
Florian Roth (Nextron Systems)
Source
github.com/chronicle/detection-rules

Detects actions that clear the local ShimCache and remove forensic evidence

Known False Positives

  • Unknown

MITRE ATT&CK Coverage

TacticTechniques
Persistence
Defense Impairment

References

Telemetry Coverage

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 PROCESS_LAUNCH (1, 4688)

  • 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

Match key
$hostname (principal.hostname)
Within
5m

Outcome

Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.

FieldExpression
risk_scoremax(if($process.principal.user.userid = "user" and $process.principal.hostname = "hostname", 0, 15))
principal_process_pidarray_distinct($process.principal.process.pid)
principal_process_command_linearray_distinct($process.principal.process.command_line)
principal_process_file_sha256array_distinct($process.principal.process.file.sha256)
principal_process_file_full_patharray_distinct($process.principal.process.file.full_path)
principal_process_product_specfic_process_idarray_distinct($process.principal.process.product_specific_process_id)
principal_process_parent_process_product_specfic_process_idarray_distinct($process.principal.process.parent_process.product_specific_process_id)
target_process_pidarray_distinct($process.target.process.pid)
target_process_command_linearray_distinct($process.target.process.command_line)
target_process_file_sha256array_distinct($process.target.process.file.sha256)
target_process_file_full_patharray_distinct($process.target.process.file.full_path)
target_process_product_specfic_process_idarray_distinct($process.target.process.product_specific_process_id)
principal_user_useridarray_distinct($process.principal.user.userid)
log_typearray_distinct(strings.concat($process.metadata.log_type,"/",$process.metadata.product_event_type))

Back to all YARA-L rules