Detection rules › Splunk

ASL AWS IAM Assume Role Policy Brute Force

Status
production
Severity
medium
Group by
"actor.user.uid", _time
Author
Patrick Bareiss, Splunk
Source
github.com/splunk/security_content

The following analytic detects multiple failed attempts to assume an AWS IAM role, indicating a potential brute force attack. It leverages AWS CloudTrail logs to identify MalformedPolicyDocumentException errors with a status of failure and filters out legitimate AWS services. This activity is significant as repeated failures to assume roles can indicate an adversary attempting to guess role names, which is a precursor to unauthorized access. If confirmed malicious, this could lead to unauthorized access to AWS resources, potentially compromising sensitive data and services.

Known false positives

  • This detection will require tuning to provide high fidelity detection capabilties. Tune based on src addresses (corporate offices, VPN terminations) or by groups of users.

MITRE ATT&CK coverage

TacticTechniques
Credential Access
Discovery

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

name: ASL AWS IAM Assume Role Policy Brute Force
id: 726959fe-316d-445c-a584-fa187d64e295
version: 8
creation_date: '2022-10-04'
modification_date: '2026-05-13'
author: Patrick Bareiss, Splunk
status: production
type: TTP
description: The following analytic detects multiple failed attempts to assume an AWS IAM role, indicating a potential brute force attack. It leverages AWS CloudTrail logs to identify `MalformedPolicyDocumentException` errors with a status of `failure` and filters out legitimate AWS services. This activity is significant as repeated failures to assume roles can indicate an adversary attempting to guess role names, which is a precursor to unauthorized access. If confirmed malicious, this could lead to unauthorized access to AWS resources, potentially compromising sensitive data and services.
data_source:
    - ASL AWS CloudTrail
search: |-
    `amazon_security_lake` api.operation="AssumeRole" "api.response.error"=AccessDenied
      | bucket _time span=1h
      | stats count as failures min(_time) as firstTime max(_time) as lastTime values(api.operation) as api.operation values(api.service.name) as api.service.name values(http_request.user_agent) as http_request.user_agent values(src_endpoint.ip) as src_ip values(actor.user.account.uid) as actor.user.account.uid values(cloud.provider) as cloud.provider values(cloud.region) as cloud.region
        BY _time actor.user.uid
      | where failures >= 3
      | rename actor.user.uid as user api.operation as action api.service.name as dest http_request.user_agent as user_agent src_endpoint.ip as src actor.user.account.uid as vendor_account cloud.provider as vendor_product cloud.region as vendor_region
      | `security_content_ctime(firstTime)`
      | `security_content_ctime(lastTime)`
      | `asl_aws_iam_assume_role_policy_brute_force_filter`
how_to_implement: The detection is based on Amazon Security Lake events from Amazon Web Services (AWS), which is a centralized data lake that provides security-related data from AWS services. To use this detection, you must ingest CloudTrail logs from Amazon Security Lake into Splunk. To run this search, ensure that you ingest events using the latest version of Splunk Add-on for Amazon Web Services (https://splunkbase.splunk.com/app/1876) or the Federated Analytics App.
known_false_positives: This detection will require tuning to provide high fidelity detection capabilties. Tune based on src addresses (corporate offices, VPN terminations) or by groups of users.
references:
    - https://www.praetorian.com/blog/aws-iam-assume-role-vulnerabilities/
    - https://rhinosecuritylabs.com/aws/assume-worst-aws-assume-role-enumeration/
    - https://www.elastic.co/guide/en/security/current/aws-iam-brute-force-of-assume-role-policy.html
finding:
    title: User $user$ has caused multiple failures with errorCode AccessDenied, which potentially means adversary is attempting to identify a role name.
    entity:
        field: user
        type: user
        score: 50
threat_objects:
    - field: src_ip
      type: ip_address
analytic_story:
    - AWS IAM Privilege Escalation
    - Scattered Lapsus$ Hunters
asset_type: AWS Account
mitre_attack_id:
    - T1580
    - T1110
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: cloud
security_domain: access

Stages and Predicates

Stage 1: search

`amazon_security_lake` api.operation="AssumeRole" "api.response.error"=AccessDenied

Stage 2: bucket

| bucket _time span=1h

Stage 3: stats

| stats count as failures min(_time) as firstTime max(_time) as lastTime values(api.operation) as api.operation values(api.service.name) as api.service.name values(http_request.user_agent) as http_request.user_agent values(src_endpoint.ip) as src_ip values(actor.user.account.uid) as actor.user.account.uid values(cloud.provider) as cloud.provider values(cloud.region) as cloud.region
    BY _time actor.user.uid

Stage 4: where

| where failures >= 3

Stage 5: rename

| rename actor.user.uid as user api.operation as action api.service.name as dest http_request.user_agent as user_agent src_endpoint.ip as src actor.user.account.uid as vendor_account cloud.provider as vendor_product cloud.region as vendor_region

Stage 6: search

| `security_content_ctime(firstTime)`

Stage 7: search

| `security_content_ctime(lastTime)`

Stage 8: search

| `asl_aws_iam_assume_role_policy_brute_force_filter`

Indicators

These rows show field, operator, and value matches.