Detection rules › YARA-L

YARA-L rules: 1w

Hunt for Groups endpoint requests in the Microsoft Graph API

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

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 NETWORK_HTTP

  • 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

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($api.metadata.id)
requesting_user_guidarray_distinct($api.principal.user.userid)
requesting_iparray_distinct($api.principal.ip)
user_agentarray_distinct($api.network.http.user_agent)
locationarray_distinct($api.principal.location.name)
target_application_id_guidarray_distinct($api.target.resource.product_object_id)
session_idarray_distinct($api.network.session_id)
target_urlarray_distinct($api.target.url)

Okta ThreatInsight Login Failure With High Unknown Users

#
Severity
medium
Type
Alert
Time window
1h
Match by
ip, userid
Author
Google Cloud Security
Source
github.com/chronicle/detection-rules

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

TacticTechniques
Credential Access

References

Telemetry Coverage

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_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 USER_UNCATEGORIZED

  • metadata.product_event_type = "security.threat.detected"
  • security_result.category_details matches "Login failures with high unknown users count"

Match and Correlation

Match key
$ip (principal.ip), $userid (principal.user.userid)
Within
1h

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($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_agentarray_distinct($login.network.http.user_agent)
principal_iparray_distinct($login.principal.ip)
principal_ip_countryarray_distinct($login.principal.ip_geo_artifact.location.country_or_region)
principal_ip_statearray_distinct($login.principal.ip_geo_artifact.location.state)
principal_ip_cityarray_distinct($login.principal.location.city)
security_result_summaryarray_distinct($login.security_result.summary)

RestrictedAdminMode Registry Value Tampering

#
Severity
high
Type
Detection
Time window
5m
Match by
hostname
Author
frack113
Source
github.com/chronicle/detection-rules

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

TacticTechniques
Persistence
Defense Impairment

References

Telemetry Coverage

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 REGISTRY_MODIFICATION (13, 4657)

  • target.registry.registry_key matches "System\\CurrentControlSet\\Control\\Lsa\\DisableRestrictedAdmin$"

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($reg.principal.user.userid = "user" and $reg.principal.hostname = "hostname", 0, 15))
principal_hostnamearray_distinct($reg.principal.hostname)
principal_process_pidarray_distinct($reg.principal.process.pid)
principal_process_file_full_patharray_distinct($reg.principal.process.file.full_path)
principal_process_product_specific_process_idarray_distinct($reg.principal.process.product_specific_process_id)
principal_user_useridarray_distinct($reg.principal.user.userid)
target_registry_keyarray_distinct($reg.target.registry.registry_key)
target_registry_value_dataarray_distinct($reg.target.registry.registry_value_data)
log_typearray_distinct(strings.concat($reg.metadata.log_type,"/",$reg.metadata.product_event_type))

Suspicious Download Via Certutil.EXE

#
Severity
medium
Type
Detection
Time window
5m
Match by
hostname
Author
Florian Roth (Nextron Systems), Jonhnathan Ribeiro, oscd.community, Nasreddine Bencherchali (Nextron Systems)
Source
github.com/chronicle/detection-rules

Detects the execution of certutil with certain flags that allow the utility to download files

Known False Positives

  • Unknown

MITRE ATT&CK Coverage

References

Telemetry Coverage

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

  • 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

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