Detection rules › YARA-L

YARA-L rules: 26

AWS IAM Compromised Key Quarantine Policy Attached

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

Detect when the AWS managed policy AWSCompromisedKeyQuarantine has been attached to a user, role, or group. It is applied by the AWS team in the event that the credentials of an IAM user has been compromised or publicly exposed.

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

Rule Body

// Copyright 2023 Google LLC. Licensed under Apache-2.0.

rule aws_iam_compromised_key_quarantine_policy_attached {

  meta:
    author = "Google Cloud Security"
    description = "Detect when the AWS managed policy AWSCompromisedKeyQuarantine has been attached to a user, role, or group. It is applied by the AWS team in the event that the credentials of an IAM user has been compromised or publicly exposed."
    rule_id = "mr_3ac90268-e8bd-41b7-8fc1-e10ee165df8e"
    rule_name = "AWS IAM Compromised Key Quarantine Policy Attached"
    mitre_attack_tactic = "Credential Access"
    mitre_attack_technique = "Unsecured Credentials"
    mitre_attack_url = "https://attack.mitre.org/techniques/T1552/"
    mitre_attack_version = "v13.1"
    type = "Alert"
    data_source = "AWS CloudTrail"
    platform = "AWS"
    severity = "High"
    priority = "High"

  events:
    $cloudtrail.metadata.vendor_name = "AMAZON"
    $cloudtrail.metadata.product_name = "AWS CloudTrail"
    (
        $cloudtrail.metadata.product_event_type = "AttachUserPolicy" or
        $cloudtrail.metadata.product_event_type = "AttachGroupPolicy" or
        $cloudtrail.metadata.product_event_type = "AttachRolePolicy"
    )
    $cloudtrail.target.resource.name = /AWSCompromisedKeyQuarantine/
    $cloudtrail.security_result.action = "ALLOW"

  outcome:
    $risk_score = max(75)
    $mitre_attack_tactic = "Credential Access"
    $mitre_attack_technique = "Unsecured Credentials"
    $mitre_attack_technique_id = "T1552"
    $event_count = count_distinct($cloudtrail.metadata.id)
    $network_http_user_agent = $cloudtrail.network.http.user_agent
    $principal_ip = array_distinct($cloudtrail.principal.ip)
    $principal_ip_country = array_distinct($cloudtrail.principal.ip_geo_artifact.location.country_or_region)
    $principal_ip_state = array_distinct($cloudtrail.principal.ip_geo_artifact.location.state)
    $principal_user_display_name = $cloudtrail.principal.user.user_display_name
    $recipient_aws_account_id = $cloudtrail.additional.fields["recipientAccountId"]
    $aws_region = $cloudtrail.principal.location.name
    $target_resource_name = $cloudtrail.target.resource.name
    $target_resource_product_object_id = $cloudtrail.target.resource.product_object_id

  condition:
    $cloudtrail
}

YARA-L Rule Structure

Condition

Fires when at least one $cloudtrail event.

Events

$cloudtrail

  • metadata.product_event_type is one of:
    • AttachUserPolicy
    • AttachGroupPolicy
    • AttachRolePolicy
  • target.resource.name matches "AWSCompromisedKeyQuarantine"
  • security_result.action = "ALLOW"

Outcome

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

FieldExpression
risk_scoremax(75)
event_countcount_distinct($cloudtrail.metadata.id)
network_http_user_agent$cloudtrail.network.http.user_agent
principal_iparray_distinct($cloudtrail.principal.ip)
principal_ip_countryarray_distinct($cloudtrail.principal.ip_geo_artifact.location.country_or_region)
principal_ip_statearray_distinct($cloudtrail.principal.ip_geo_artifact.location.state)
principal_user_display_name$cloudtrail.principal.user.user_display_name
recipient_aws_account_id$cloudtrail.additional.fields["recipientAccountId"]
aws_region$cloudtrail.principal.location.name
target_resource_name$cloudtrail.target.resource.name
target_resource_product_object_id$cloudtrail.target.resource.product_object_id

Enumeration of users and group membership observed in the Microsoft Graph API

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

Identify the enumeration of both the users and members of groups in the Entra ID tenant. While these are two separate functions in GraphRunner, they are often run in support of one another.

References

Rule Body

// Copyright 2025 Google LLC. Licensed under Apache-2.0.

 rule ms_graph_user_and_group_enumeration {

  meta:
    author = "Google Cloud Security"
    description = "Identify the enumeration of both the users and members of groups in the Entra ID tenant. While these are two separate functions in GraphRunner, they are often run in support of one another."
    rule_id = "mr_1c65d59f-9b9c-4023-b98c-7d6cff503b1b"
    rule_name = "Enumeration of users and group membership observed in the Microsoft Graph API"
    assumption = "The user endpoint is frequently called and not ideal to be detected on, however enumeration of users within a security group often occurs with the users being enumerated. This can have applicability beyond graphrunner but additonal tuning may be needed."
    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:
    $group_api.metadata.event_type = "NETWORK_HTTP"
    $group_api.metadata.product_event_type = "Microsoft Graph Activity"
    re.regex($group_api.target.url, `https://graph\.microsoft\.com/v1\.0/groups/.*/members`) nocase
    //Can be tuned for specific User Agent strings being used - by default GraphRunner does not forge the UA for this action
    //$group_api.network.http.user_agent = /PowerShell/ nocase
    $group_api.network.http.method = "GET"
    $group_api.network.http.response_code = 200
    $group_api.principal.ip = $ip
    $group_api.network.session_id = $session
    re.capture($group_api.target.url, `https://graph\.microsoft\.com/v1\.0/groups/(.*)/members`) = $group_guid

    //Used to force the users to be requested before the groups which occurs in GraphRunner, but from a recon perspective, this doesn't have to happen in this order
    $user_api.metadata.event_timestamp.seconds < $group_api.metadata.event_timestamp.seconds

    $user_api.metadata.event_type = "NETWORK_HTTP"
    $user_api.metadata.product_event_type = "Microsoft Graph Activity"
    $user_api.target.url = "https://graph.microsoft.com/v1.0/users" nocase
    $user_api.network.http.method = "GET"
    $user_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
    //$user_api.network.http.user_agent = /PowerShell/ nocase
    $user_api.principal.ip = $ip
    $user_api.network.session_id = $session

  match:
    $ip, $session over 15m

  outcome:
    $risk_score = 35
    $event_count = count_distinct($group_api.metadata.id) + count_distinct($user_api.metadata.id)
    $requesting_user_guid = array_distinct($group_api.principal.user.userid)
    $requesting_ip = array_distinct($group_api.principal.ip)
    $user_agent = array_distinct($group_api.network.http.user_agent)
    $location = array_distinct($group_api.principal.location.name)
    $target_application_id_guid = array_distinct($group_api.target.resource.product_object_id)
    $session_id = array_distinct($group_api.network.session_id)
    $group_count = count_distinct($group_guid)
    $group_list = array_distinct($group_guid) //limited to 25 groups

  condition:
    $group_api and $user_api
}

YARA-L Rule Structure

Condition

Fires when at least one $group_api event in the 15m window and at least one $user_api event in the 15m window.

Events

$user_api NETWORK_HTTP

  • metadata.product_event_type = "Microsoft Graph Activity"
  • target.url = "https://graph.microsoft.com/v1.0/users"
  • network.http.method = "GET"
  • network.http.response_code = "200"

$group_api NETWORK_HTTP

  • metadata.product_event_type = "Microsoft Graph Activity"
  • target.url matches "https://graph\.microsoft\.com/v1\.0/groups/.*/members"
  • network.http.method = "GET"
  • network.http.response_code = "200"

Match and Correlation

Order
$user_api before $group_api
Match key
$ip (principal.ip), $session (network.session_id)
Within
15m

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($group_api.metadata.id) + count_distinct($user_api.metadata.id)
requesting_user_guidarray_distinct($group_api.principal.user.userid)
requesting_iparray_distinct($group_api.principal.ip)
user_agentarray_distinct($group_api.network.http.user_agent)
locationarray_distinct($group_api.principal.location.name)
target_application_id_guidarray_distinct($group_api.target.resource.product_object_id)
session_idarray_distinct($group_api.network.session_id)
group_countcount_distinct($group_guid)
group_listarray_distinct($group_guid)

Back to all YARA-L rules