Pentest Journeys
Connect
  • Welcome!
  • Boxes
    • Categories
    • Starting Point
      • Unified
      • Three
    • Easy
      • Forest
      • Sauna
      • Active
      • Broker
      • Return
      • Timelapse
      • Support
      • Nibbles
      • Keeper
      • CozyHosting
      • Devvortex
      • Lame
      • FunBoxEasyEnum
      • Inclusiveness
      • Potato
      • Sumo
    • Medium
      • Resolute
      • Cascade
      • Monteverde
      • Intelligence
      • StreamIO
      • Loly
    • Hard
      • Mantis
      • Blackfield
      • Reel
    • Insane
      • Sizzle
      • Multimaster
      • Rebound
  • Cloud
    • Public Snapshots
  • TL;DR
    • Active Directory
      • AD Authentication
      • Access Controls
      • Recon
      • Groups
        • Account Operators
        • Backup Operators
        • DnsAdmins
        • Exchange Windows Permissions
        • Server Operators
      • Privileges
        • SeBackupPrivilege
        • SeImpersonatePrivilege
      • Permissions
        • AddSelf
        • DCSync
        • ForceChangePassword
        • FullControl
        • GenericAll
        • GenericWrite
        • Owns
        • ReadGMSAPassword
        • ReadLAPSPassword
        • WriteDACL
        • WriteOwner
      • Attacks
        • Password Spraying
        • ASREPRoasting
        • Kerberoasting
        • Silver Tickets
        • DCSync
        • Delegation
          • Unconstrained
          • Constrained
          • Resource-Based
        • Local SAM Dump
        • NTLMv2
        • Services
        • Scheduled Tasks
        • Exploits
      • Lateral Movement
        • WMI & WinRM
        • PsExec
        • Pass-the-Hash
        • Overpass-the-Hash
        • Pass-the-Ticket
        • DCOM
        • SSP Injection
      • Persistence
        • Golden Ticket
        • Shadow Copies
    • Web
      • Common Findings
        • Security Headers
        • Cookie Flags
        • SSL/TLS
      • Authentication
        • Broken Reset Logic
        • Brute Force Attacks
        • Rate Limiting
        • Session Tokens
        • MFA
        • JWTs
      • Authorization
        • IDOR / BOLA
        • IDOR / BFLA
        • Weak Access Controls
        • Automated A-B Testing
      • Injections
        • SQLi
          • SQLi 101
          • In Band
          • Blind
          • NoSQLi
          • Second Order
          • Other
        • XSS
          • XSS 101
          • Reflected
          • Stored
          • DOM-Based
          • Exploitation
          • Payloads
        • CI
          • CI
          • Filters
          • Examples
        • SSTI
          • SSTI 101
          • Twig
          • Freemarker
          • Pug
          • Jinja
          • Mustache
          • Handlebars
          • Mako
          • Case Study: Craft CMS
        • XXEI
          • XML 101
          • XXEI
      • File Inclusion
        • LFI & RFI
        • RCE
      • Cross-Origin
        • Cross-Origin 101
        • CSRF
        • CORS
      • File Uploads
      • Mass Assignment
      • WebSockets
      • Open Redirects
      • Race Conditions
      • SSRF
        • Exploitation
        • Examples
    • API
      • What is an API?
      • Useful Terms
      • Collection Creation
      • Enumeration
      • Tests
        • General
        • Security Misconfigurations
        • Authorization
          • BOLA
          • BFLA
        • Authentication
          • BFAs
          • Tokens
          • JWTs
            • Entropy Analysis
            • Signature Validation
            • Weak Signature
            • Header Injection
            • Algorithm Confusion
        • Excessive Data Exposure
        • HTTP Verb Tampering
        • Content Type Tampering
        • Improper Asset Management
        • Mass Assignment
        • SSRF
        • Unrestriced Resource Consumption
        • Unrestricted Access to Sensitive Business Flows
        • Unsafe API Consumption
    • Infra
      • Windows
      • Linux
      • FreeBSD
    • Pivoting
      • Networking 101
      • Port Foward
      • SSH Tunelling
      • Deep Packet Inspection
        • HTTP Tunneling
        • DNS Tunneling
    • Social Engineering
      • Phising
    • Cloud
      • AWS
        • Recon
    • Code Review
  • Tools
    • Web
      • Web Checklist
      • API
        • mitmweb
        • KiteRunner
        • Arjun
        • jwt_tool
      • Dirbusting
        • Fuff
        • Dirsearch
        • GoBuster
        • Wfuzz
      • Cloud
        • AWS
      • cURL
      • Hydra
      • Hakrawler
      • amass
      • WAFs
      • WhatWeb
      • Creds
      • SQLMap
      • GoWitness
      • Web Servers
        • Apache
        • Nginx
        • IIS
      • Frameworks
        • Spring
      • CMS
        • WordPress
        • Joomla
        • DNN
        • Umbraco
        • RiteCMS
      • DevOps
        • GitLab
        • Git Tools
      • BurpSuite
    • Infra
      • pspy
    • Port Scanners
      • Nmap
      • Rustscan
      • Arp-Scan
      • Netcat
      • PowerShell
    • Active Directory
      • netexec
      • impacket
      • mimikatz
      • Hounds
      • PowerView
      • SysInternals
      • net.exe
      • ldapsearch
      • BloodyAD
      • PowerView.py
      • Rubeus
      • DPAT
      • PingCastle
      • PowerUp
      • runas
      • Kerbrute
    • Passwords
      • HashID
      • Hashcat
      • John
      • DomainPasswordSpray
      • Credential Enum
    • Searchsploit
    • Metasploit
      • 101
      • Payloads
      • Post-Exploitation
      • Resource Scripts
    • Usernames
    • Vulnerability Scanners
      • Nuclei
      • Nikto
    • Text
      • jq
      • grep
      • awk
      • sed
      • tr
      • printf
    • Output
      • tee
    • Pivoting
      • Ligolo-ng
      • Sshuttle
    • Shells
      • Reverse Shells
      • Webshells
      • Upgrade
      • Listeners
        • Socat
        • Pwncat
        • Nc
    • Traffic Capture
    • File Transfers
    • Crypto
    • Files
    • Images
    • Evil-WinRM
    • KeePass
    • Random Scripts
  • Services
    • TCP
      • Remote Access
        • SSH (22)
        • RDP (3389)
        • WinRM (5985,5986)
      • Shares
        • FTP (21)
        • NFS (111, 2049)
        • SMB (139, 445)
      • LDAP (389, 636)
      • DNS (53)
      • SMTP (25,587)
      • DISTCC (3632)
      • AFS (1978)
      • DBMS
        • SQL
          • MSSQL (1433)
          • Oracle (1521)
          • MySQL (3306)
          • MariaDB (3306)
          • PostgreSQL (5432)
        • NoSQL
          • Aerospike (3000-3005)
          • MongoDB (27017)
    • UDP
      • SNMP (161)
  • OTHER
    • Exploits
      • Screen
    • CLIs
      • CMD
      • PowerShell
  • Package Managers
    • vevn
    • uv
  • Blue Team Stuff
    • Logs
      • System Logs
      • Apache2
      • Volatile Data
    • Traffic Analysis
      • Wireshark
Powered by GitBook
On this page
  • CSP Enumeration
  • Domain-based
  • S3 Buckets
  • API Enumeration
  • AccountID Discovery
  • Cross-Account Access
  • Initial IAM Recon
  • Identity Details
  • IAM Permissions
  • IAM Resources Recon
  • Explicing Deny Bypass
  • Processing API Responses
  • Automated Enumeration
  • Extracting Insights

Was this helpful?

  1. TL;DR
  2. Cloud
  3. AWS

Recon

CSP Enumeration

Domain-based

Domain reconnaissance is a passive technique used early in attacks to gather public data without alerting the target. Starting with a domain name (e.g., example.com), the goal is to identify infrastructure, hosting details, and subdomains. A recursive approach is essential—each discovery can lead to more.

Every domain has authoritative name servers that store its DNS records. Identifying them reveals who manages the domain’s DNS, often pointing to the cloud provider or registrar. For example, name server queries might show the , which can be verified via .

# Query the domain's authoritative name servers
$ host -t ns example.com
example.com name server ns-1456.awsdns-00.co.uk.
example.com name server ns-533.awsdns-00.net.

# Verify the organization managing the name server
$ whois ns-1456.awsdns-00.co.uk | grep "Registrant Organization"
Registrant Organization: Amazon Technologies, Inc.

To identify where a site is hosted, we can resolve the domain and then perform a reverse IP lookup. In this case, the result shows the service is hosted on an AWS EC2 instance.

# Resolve the domain
$ host www.example.com
www.example.com has address 54.73.167.99

# Reverse-lookup the IP address
$ host 54.73.167.99
80.127.74.52.in-addr.arpa domain name pointer ec2-54.73.167.99.compute-1.amazonaws.com

# Confirm ownership
$ whois 54.73.167.99 | grep "OrgName"
OrgName: Amazon Technologies Inc.

To discover more subdomains, automated tools like dnsenum can be used.

$ dnsenum example.com --threads 100
-----   example.com   -----

Host's addresses:
example.com.   A   54.73.167.99

Name Servers:
ns-1456.awsdns-00.co.uk.   A   205.251.198.0
...

Trying Zone Transfer for example.com on ns-533.awsdns-00.net ...
AXFR record query failed: corrupt packet
...

Brute forcing with /usr/share/dnsenum/dns.txt:
mail.example.com.   A   54.73.167.99
www.example.com.    A   54.73.167.99

S3 Buckets

Public cloud infrastructure often uses predictable hostnames that reveal the service in use. For example, a reverse DNS lookup might return ec2-18-102-34-210.compute-1.amazonaws.com, indicating an AWS EC2 instance. Browser developer tools can aid in recon. In the Network tab, each row lists the file name and the domain it originated, such as:

https://s3.amazonaws.com/example-assets-public-mnzpytrl/sites/www/images/example.jpg

The above URL:

  • suggests the use of

  • the bucket name: example-assets-public-mnzpytrl

  • the object key: sites/www/images/randomphoto.jpg.

Visiting the bucket URL without the object path may reveal misconfigured permissions, such as directory listing, indicating that the bucket is publicly readable.

S3 permissions allow public access at the object level even if the bucket is private.

cloud_enum -k example-assets-public-mnzpytrl -qs --disable-azure --disable-gcp

Or generate bucket naming variations with:

$ for key in "public" "private" "dev" "prod"; do
    echo "example-assets-$key-mnzpytrl"
done | tee /tmp/keyfile.txt

$ cloud_enum -kf /tmp/keyfile.txt -qs --disable-azure --disable-gcp

API Enumeration

  • Web Portals: Browser-based interfaces for managing cloud services. Users authenticate with credentials like usernames, passwords, and often . This method is user-friendly and commonly used by individuals and administrators.

  • APIs: Designed for programmatic access, APIs enable developers and automation tools to manage services without using a browser. Though protected by API keys, tokens, or cloud credentials, they are publicly exposed.

Despite requiring authentication, APIs can aid attackers during reconnaissance. An attacker might register a legitimate account and use valid credentials to explore API responses, discovering details about misconfigurations, exposed resources, or other tenants. This doesn’t involve bypassing controls but rather abusing intended, documented functionality — known as API abuse.

is the gatekeeper of AWS accounts. It controls users, roles, permissions, and access policies within an AWS account. Each user created under an IMA is called an IAM user. IAM defines what a user can do—such as accessing storage, launching servers, or using APIs.

Using named profiles in the AWS is a best practice for managing credentials across different IAM users. It allows easy switching between identities for testing access and behavior. Once configured, every command we run must include the profile to use, e.g. --profile attacker, so AWS CLI knows which credentials to use. After configuration, we can verify the setup by asking AWS to tell us who we are using the .

# Configure a named profile
$ aws configure --profile attacker
AWS Access Key ID [None]: AK<REDACTED>UM
AWS Secret Access Key [None]: uC5<REDACTED>w+l
Default region name [None]: us-east-1
Default output format [None]: json

# List profile's information
$ aws --profile attacker sts get-caller-identity
{
    "UserId": "AI<REDACTED>UO",
    "Account": "79<REDACTED>25",
    "region": "us-east-1",
    "Arn": "arn:aws:iam::79<REDACTED>25:user/attacker"
}

The Amazon Resource Name (ARN) uniquely identifies the profile in AWS.

AccountID Discovery

Cloud platforms like AWS let users share resources internally or publicly. While this enables collaboration, misconfigurations can expose sensitive data. Some resources are meant to be shared, but others may become publicly accessible by mistake. Under the AWS Shared Responsibility Model, securing shared data is the customer’s job—a common source of risk. Unlike web apps with visible URLs, these resources typically lack public endpoints. They’re discoverable only by querying the AWS API, often using the AWS CLI.

Via Public AMIs

An AMI is a blueprint for launching EC2 instances. It includes an and may also contain apps, configs, or user data. AMIs can be shared publicly—intentionally (e.g., open-source) or unintentionally due to misconfigurations.

# Enumerate public AMIs owned by Amazon
aws --profile attacker ec2 describe-images --owners amazon --executable-users all

We can enumerate the target's AccountID via filtering the AMI's metadata.

# Filter by desciption
$ aws --profile attacker ec2 describe-images --executable-users all --filters "Name=description,Values=*exampleorg*"

# Filter by name
$ aws --profile attacker ec2 describe-images --executable-users all --filters "Name=name,Values=*exampleorg*"
{
    "Images": [
            ...
            "OwnerId": "792162019125",
            ...
            "Name": "ExampleOrg Base AMI",

Using the same principles, we can search for publicly available and snapshots.

# Enumerating EBS snapshots (EC2 service) filtering by description 
$ aws --profile attacker ec2 describe-snapshots --filters "Name=description,Values=*x7331lab*"
# Enumerating EBS snapshots by Account ID
$ aws ec2 describe-snapshots --profile attacker --owner-ids 792162019125

# RDS snapshots (RDS service)
$ aws --profile attacker rds describe-db-snapshots --query "DBSnapshots[?contains(DBSnapshotIdentifier, 'x7331lab')]"

Via IAM Policies

An attacker using their own AWS account can try to access misconfigured S3 buckets owned by another organization. If access is granted based on loose or overly broad policies, it may reveal sensitive data or even the account ID of the target. The attacker’s approach involves:

  1. Creating a new IAM user to simulate a low-privilege attacker

  2. Crafting a permissive IAM policy which includes a condition that matches certain account IDs

  3. Using that policy to test access against external S3 buckets

  4. Iterating on the policy to discover the correct account ID range

The enum user simulates a clean slate attacker with no permissions.

# Create a new IAM user named 'enum'
aws --profile attacker iam create-user --user-name enum

# Generate access keys for this user
aws --profile attacker iam create-access-key --user-name enum

# Configure a new AWS CLI profile for the user
aws configure --profile enum

# Check identity (to confirm profile setup)
aws sts get-caller-identity --profile enum

# List accessible S3 buckets
$ aws --profile enum s3api list-buckets

# Check the permissions of a private S3 bucket
aws --profile enum s3api get-bucket-acl --bucket exampleorg-assets-private-dayaiiov

An error occurred (AccessDenied) when calling the GetBucketAcl operation: User: arn:aws:iam::792162019125:user/enum is not authorized to perform: s3:GetBucketAcl on resource: "arn:aws:s3:::exampleorg-assets-private-dayaiiov" because no identity-based policy allows the s3:GetBucketAcl action

The attacker writes a policy allowing access only if the resource's AWS account ID starts with a specific prefix, in this case 0.

policy-s3-read.json
{
     "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowResourceAccount",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject"
            ],
            "Resource": "*",
            "Condition": {
                "StringLike": {"s3:ResourceAccount": ["0*"]}
            }
        }
    ]
}
# Attach this policy to the user 'enum'
aws --profile attacker iam put-user-policy \
    --user-name enum \
    --policy-name s3-read \
    --policy-document file://policy-s3-read.json

# Confirm the policy is attached
aws --profile attacker iam list-user-policies --user-name enum

The attacker now changes the wildcard condition to match different account ID prefixes until access is granted.

# List the attacker's account ID (for testing)
aws sts get-caller-identity --profile enum
...
    "Account": "792162019125",
...

# Try accessing the target bucket again
aws --profile enum s3 ls exampleorg-assets-private-dayaiiov

An error occurred (AccessDenied) when calling the ListObjectsV2 operation:
...

Update the policy to try a different prefix:

policy-s3-read.json
...
                "StringLike": {"s3:ResourceAccount": ["7*"]}
...
# Re-attach the updated policy
aws --profile attacker iam put-user-policy \
    --user-name enum \
    --policy-name s3-read \
    --policy-document file://policy-s3-read.json

# Try again
aws --profile enum s3 ls exampleorg-assets-private-dayaiiov
                           PRE sites/

This time the command succeeds (e.g., returns a directory listing like PRE sites/), thus, the attacker has confirmed that the resource is owned by an account beginning with 7. By incrementally modifying the account ID prefix, the attacker can continue the process:

"StringLike": { "s3:ResourceAccount": ["10*"] }
"StringLike": { "s3:ResourceAccount": ["11*"] }
...
"StringLike": { "s3:ResourceAccount": ["19*"] }

Cross-Account Access

Previously, we extracted an AWS account ID. Now, we’ll explore how to enumerate internal IAM identities (users or roles) in a known account by analyzing policy-based API responses. In AWS, cross-account access allows resources to be shared with just some specific external accounts, rather than the public. IAM policies use a Principal field to specify the target identity (e.g., user or role). If that identity doesn’t exist, AWS returns an error—this can be used to verify if an identity is valid.

# Create a private S3 bucket in the attacker's account
$ aws --profile attacker s3 mb s3://exampleorg-dummy-bucket-$RANDOM-$RANDOM-$RANDOM
make_bucket: exampleorg-dummy-bucket-26456-5922-26678

Define a bucket policy targeting a specific user in the victim account.

grant-s3-bucket-read.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowUserToListBucket",
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::exampleorg-dummy-bucket-26456-5922-26678",
      "Principal": {
        "AWS": ["arn:aws:iam::792162019125:user/enum"]
      },
      "Action": "s3:ListBucket"
    }
  ]
}
# Apply the policy
$ aws --profile attacker s3api put-bucket-policy --bucket exampleorg-dummy-bucket-26456-5922-26678 --policy file://grant-s3-bucket-read.json

An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Invalid principal in policy

If no errors occur, the policy is applied successfully, meaning the enum user exists in the target account. If we apply the policy for a non-existent user, we receive an error message.

In a similar manner, IAM roles can also be discovered by setting up trust policies and observing the responses. We can use pacu to achieve that.

$ pacu
What would you like to name this new session? exampleorg
Session exampleorg created.

# Import attacker credentials
Pacu (exampleorg:No Keys Set) > import_keys attacker

# List recon modules
Pacu > ls
[Category: RECON_UNAUTH]
  iam__enum_roles

# Run the role enumeration module
Pacu > run iam__enum_roles --word-list /tmp/role-names.txt --account-id 792162019125
...
[iam__enum_roles] Targeting account ID: 792162019125
[iam__enum_roles] Starting role enumeration...
[iam__enum_roles]   Found role: arn:aws:iam::792162019125:role/lab_admin
[iam__enum_roles] Found 1 role(s):
[iam__enum_roles]     arn:aws:iam::792162019125:role/lab_admin

[iam__enum_roles] {
  "Credentials": {
    "AccessKeyId": "AS...4W",
    "SecretAccessKey": "Hl...hx",
    "SessionToken": "Fw...MA==",
    "Expiration": "2025-05-04 12:33:23+00:00"
  },
  "AssumedRoleUser": {
    "AssumedRoleId": "AR...pw",
    "Arn": "arn:aws:sts::792162019125:assumed-role/lab_admin/mg1ikNEzYJeAIdhHJgpw"
  }
}
Cleaning up the PacuIamEnumRoles-9Wh71 role.
...
# Use assumed role credentials
$ export AWS_ACCESS_KEY_ID=AS...4W
$ export AWS_SECRET_ACCESS_KEY=Hl...hx
$ export AWS_SESSION_TOKEN=Fw...MA==

# Example action using the assumed role
$ aws ec2 describe-vpcs --region us-east-1

This approach allows attackers to enumerate and even assume roles within a target AWS account—often serving as a pivot for deeper access.

Initial IAM Recon

Identity Details

After compromising AWS credentials, attackers begin by exploring what the account can access—all while staying within valid permissions to avoid detection. Though AWS logs actions via CloudTrail, alerts only trigger if properly configured. As a result, legitimate-but-curious activity often goes unnoticed.

Assuming we’ve obtained a set of AWS access keys (target), our first goal is to determine whether they are still valid, what account they belong to, and ideally do so without triggering logs in the target's CloudTrail. The most common way to verify credentials is via the get-caller-identity subcommand. However, this action is always allowed (even if explicitly denied) and always logged in the target’s CloudTrail.

# Confirm the validity of target's credentials
$ aws --profile target sts get-caller-identity
{
  "UserId": "AIDA5DLOD4WNRXMAPABP6",
  "Account": "900563461531",
  "Arn": "arn:aws:iam::900563461531:user/support/clouddesk-plove"
}

For stealthier reconnaissance, we can use the get-access-key-info subcommand. This only reveals the account ID tied to the access key, and it logs only to the caller's account, not the account associated with the key. Therefore, we run it from a separate AWS account (e.g., an attacker-controlled one). Below is an example using an “attacker” profile configured with separate credentials:

# Assume we are using the 'attacker' profile from a different AWS account
$ aws configure --profile attacker
AWS Access Key ID [None]: AK<REDACTED>ATTACKER
AWS Secret Access Key [None]: hI<REDACTED>123
Default region name [None]: us-east-1
Default output format [None]: json

# Now check the compromised access key (belonging to the target account)
$ aws --profile attacker sts get-access-key-info --access-key-id AK<REDACTED>TARGET
{
    "Account": "900563461531"
}

Another stealthy approach for revealing identity details is via failed API calls; these aren't logged by default in Cloudtrail.

aws --profile target lambda invoke --function-name arn:aws:lambda:us-east-1:123456789012:function:nonexistent-function outfile

An error occurred (AccessDeniedException) when calling the Invoke operation: 
User: arn:aws:iam::900563461531:user/support/clouddesk-plove is not authorized to perform: lambda:InvokeFunction...

CloudTrail logging behavior varies by region. For example, the below command may show up in us-east-2 logs—but remain invisible in the default region (us-east-1) if not explicitly configured. Attackers exploit this gap to probe quieter regions.

aws --profile target sts get-caller-identity --region us-east-2

IAM Permissions

IAM policies determine what a user can do. Permissions may be:

  • Inline (directly attached to the user)

  • Managed (reusable, attached to multiple identities)

  • Inherited via group membership

AWS Managed Policies are pre-defined and often too permissive, allowing broad access to many resources.

# Inline policies
$ aws --profile target iam list-user-policies --user-name clouddesk-plove

# Managed policies
$ aws --profile target iam list-attached-user-policies --user-name clouddesk-plove

# Group memberships
$ aws --profile target iam list-groups-for-user --user-name clouddesk-plove
...
           "GroupName": "support",
...

# Inline group policies
$ aws --profile target iam list-group-policies --group-name support

# Managed group policies
$ aws --profile target iam list-attached-group-policies --group-name support
...
  "AttachedPolicies": [
    {
      "PolicyName": "SupportUser",
      "PolicyArn": "arn:aws:iam::aws:policy/job-function/SupportUser"
...

To see the policy conten, we must first find its exact version.

# List versions
$ aws --profile target iam list-policy-versions --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser
...
           "VersionId": "v8",
...

# Get default version
$ aws --profile target iam get-policy-version \
  --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser \
  --version-id v8

If the compromised account lack the privileges to query for IAM-related infomation, permissions can be inferred via brute-force API probing with tools such as pacu → iam__bruteforce_permissions, awsenum, and enumerate-iam .

The above tools can generate a lot of noise; a manual, service-aware approach is stealthier in sensitive environments.

IAM Resources Recon

With access to the compromised clouddesk-plove IAM user, we begin enumeration. The user belongs to the support group and inherits the AWS-managed SupportUser policy, designed for support roles. According to AWS, this policy grants broad read-only access—enough to reveal sensitive information about the environment’s security posture.

Resource Type
Name
ARN

IAM::User

clouddesk-plove

arn:aws:iam::123456789012:user/support/clouddesk-plove

IAM:Group

support

arn:aws:iam::123456789012:group/support/support

IAM::Policy

SupportUser

arn:aws:iam::aws:policy/job-function/SupportUser

We can start by retrieving and inspecting the policy to confirm IAM read permissions.

# Retrieve the policy
$ aws --profile target iam get-policy-version --policy-arn arn:aws:iam::aws:policy/job-function/SupportUser --version-id v8 | grep "iam"

                        "iam:GenerateCredentialReport",
                        "iam:GenerateServiceLastAccessedDetails",
                        "iam:Get*",
                        "iam:List*",

This allows nearly all get and list operations on IAM. We identify these using:

aws --profile target iam help | grep -E "list-|get-|generate-"

We can learn about any of the commands by running aws iam <command> help.

We can then run get-account-summary:

aws iam get-account-summary | tee account-summary.json

The output includes useful information:

  • 18 users, 20 roles, 8 groups

  • No MFA enabled (MFADevices: 0, MFADevicesInUse: 0)

  • No MFA enable for the root account (AccountMFAEnabled: 0)

This is a serious weakness—any compromised credentials are immediately usable without second-factor authentication. We continue by enumerating users, groups, and roles:

aws --profile target iam list-users | tee users.json
aws --profile target iam list-groups | tee groups.json
aws --profile target iam list-roles | tee roles.json

These commands reveal identity paths and naming conventions (e.g., admin-alice, admin) that may indicate elevated privileges and warrant deeper analysis.

To investigate permissions at a more granular level, we can list the IAM policies created within this account, limiting the scope to locally-managed, i.e., customer-managed and not AWS-managed policies, and currently-attached policies, i.e., policies attached to an IAM identity.

$ aws --profile target iam list-policies --scope Local --only-attached | tee policies.json
...
           "PolicyName": "manage-credentials",
...

One notable policy: manage-credentials—its name implies potential for key rotation or MFA manipulation, both possible paths to escalation. We can fetch all attached identity definitions using:

aws --profile target iam get-account-authorization-details --filter User Group LocalManagedPolicy Role | tee account-authorization-details.json

To run get-account-authorization-details, the account needs GetAccountAuthorizationDetails, which is rarely granted directly but often included via broader wildcards like iam:Get*.

Explicing Deny Bypass

During this phase, we enumerated the deny_challenges_access policy which is attached to our compromised user.

# List managed account policies
$ aws --profile target iam list-attached-user-policies --user-name clouddesk-plove
...
            "PolicyName": "deny_challenges_access",
...

# Retrive policy
aws --profile target iam list-policy-versions --policy-arn arn:aws:iam::12345678912:policy/deny_challenges_access

An error occurred (AccessDenied) when calling the ListPolicyVersions operation: 
... 
with an explicit deny in an identity-based policy

Explicit denies always override allows, signaling the admin intentionally blocked access to this policy. However, IAM restrictions must account for both what’s explicitly denied and what’s still implicitly allowed. Thus, when we run a broader command that retrieves all locally managed policies in the account, hoping that the forbidden policy might be included in the output.

$ aws --profile target iam get-account-authorization-details --filter LocalManagedPolicy
...
  "Statement": [
    {
      "Sid": "DenyAllIAMActionsOnChallengedResources",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/challenge": "true"
...

This applies a global Deny to all tagged resources (challenge=true). Despite the explicit block, we accessed it through broader permissions—a classic example of indirect access.

Processing API Responses

The AWS CLI returns JSON by default, which can be filtered using JMESPath—a query language tailored for JSON. This helps extract and transform key data during enumeration, especially from verbose commands like iam get-account-authorization-details.

$ aws --profile target iam get-account-authorization-details --filter User
{
    "UserDetailList": [
        {
            "Path": "/admin/",
            "UserName": "admin-alice",
            "UserId": "AIDAQOMAIGYUSSOCFCREC",
            "Arn": "arn:aws:iam::123456789012:user/admin/admin-alice",
            "GroupList": [
                "admin"
            ],
            "AttachedManagedPolicies": [],
            "Tags": []
        },
...

The output includes a UserDetailList array of user objects with fields like UserName, UserId, Arn, and GroupList. To isolate usernames:

$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[].UserName"
[
    "admin-alice",
    "admin-cbarton",
    "admin-srogers",
    "admin-tstark",
    "clouddesk-bob"
]

JMESPath queries (--query) run client-side, while filters (--filter) run server-side. To retrieve multiple fields we can use the [key1, key2, key3] or the labeled keys format {Identifier1: key1, Identifier2: key2, IdentifierN: keyN} format:

# Key format
$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[0].[UserName,Path,GroupList]"
[
    "admin-alice",
    "/admin/",
    [
        "admin"
    ]
]

# Labeled key format
$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[0].{Name: UserName,Path: Path,Groups: GroupList}"
{
    "Name": "admin-alice",
    "Path": "/admin/",
    "Groups": [
        "admin"
    ]
}

To filter users with names containing admin:

$ aws --profile target iam get-account-authorization-details --filter User --query "UserDetailList[?contains(UserName, 'admin')].{Name: UserName}"
...
    {
        "Name": "admin-alice"
    },
    {
        "Name": "admin-cbarton"
    },
...

For nested filtering—e.g., users and groups under /admin/ paths:

$ aws --profile target iam get-account-authorization-details --filter User Group --query "{Users: UserDetailList[?Path=='/admin/'].UserName, Groups: GroupDetailList[?Path=='/admin/'].{Name: GroupName}}"
...
   "Users": [
       "admin-alice"
   ],
   "Groups": [
       {
           "Name": "admin"
       }
...

Or combine conditions:

$ aws --profile target iam get-account-authorization-details --filter User Group --query "UserDetailList[?contains(UserName, 'admin') && contains(Path,'admin')]"

Automated Enumeration

# Install Pacu
sudo apt update && sudo apt install pacu

# Start a new session
pacu
What would you like to name this new session? enumlab
Pacu (enumlab:No Keys Set) >

# Import AWS credentials from CLI config
Pacu (enumlab:No Keys Set) > import_keys target
Imported keys as "imported-target"

# List available modules
Pacu (enumlab:imported-target) > ls

# View details about a specific module
Pacu (enumlab:imported-target) > help iam__enum_users_roles_policies_groups

# Run the module to enumerate IAM resources
Pacu (enumlab:imported-target) > run iam__enum_users_roles_policies_groups
...
  18 Users Enumerated
  20 Roles Enumerated
  8 Policies Enumerated
  8 Groups Enumerated
  IAM resources saved in Pacu database.

This data is stored in Pacu’s internal SQLite database.

# Check which services have stored data
Pacu (enumlab:imported-target) > services
 IAM
# View collected IAM data
Pacu (enumlab:imported-target) > data IAM

Extracting Insights

Once AWS IAM enumeration is complete, the next step is analysis—turning raw data into actionable findings. This is key for identifying privilege escalation paths or misconfigurations. We must first determine what we're looking for: elevated privileges, policy gaps, or tagging misuse.

# Query for a specific IAM user
aws --profile target iam get-account-authorization-details --filter User Group --query "UserDetailList[?UserName=='admin-alice']"

Output shows admin-alice is in admin and amethyst_admin groups, tagged with Project: amethyst. While the user has no direct policies, group memberships imply privilege inheritance.

Attribute-Based Access Control (ABAC) uses attributes (like tags) to control access, making tag analysis crucial.

# Inspect the admin group
$ aws --profile target iam get-account-authorization-details --filter User Group --query "GroupDetailList[?GroupName=='admin']"
...
        "AttachedManagedPolicies": [
            {
                "PolicyName": "AdministratorAccess",
                "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
...

# Inspect the amethyst_admin group
$ aws --profile target iam get-account-authorization-details --filter User Group --query "GroupDetailList[?GroupName=='amethyst_admin']"
...
        "AttachedManagedPolicies": [
            {
                "PolicyName": "amethyst_admin",
                "PolicyArn": "arn:aws:iam::123456789012:policy/amethyst/amethyst_admin"
...

The admin group is attached to the AWS-managed AdministratorAccess policy, and the amethyst_admin group uses the amethyst_admin custom policy.

# View the custom policy document
$ aws --profile target iam get-account-authorization-details --filter LocalManagedPolicy --query "Policies[?PolicyName=='amethyst_admin']"
...

The policy grants broad IAM permissions (e.g., iam:*) but scopes them to resources tagged with Project:amethyst or located under specific paths. While this appears restrictive, it can be dangerously permissive—especially when those tags overlap with high-privilege resources. The wildcard use (iam:*) itself raises concerns about over-permissiveness. Two privilege escalation paths emerge:

  1. Direct Access Abuse

    • Compromise admin-alice via phishing or exposed credentials

    • Exploit absence of MFA

  2. Group-Based Escalation

    • Users in the amethyst_admin group can use iam:CreateAccessKey on admin-alice

    • Add themselves to the admin group using iam:AddUserToGroup

The critical issue is that admin-alice is both tagged with Project:amethyst and a member of amethyst_admin. This allows other group members to perform privileged IAM actions on her—including creating access keys—enabling escalation from lower-privileged users to full admin access. This unintended overlap of tagging and group membership results in a serious security misconfiguration.

PreviousAWSNextCode Review

Last updated 15 days ago

Was this helpful?

Bucket naming conventions often follow patterns such as [org]-[type]-[env]-[random]. Predictable naming lets attackers guess related buckets (e.g., example-assets-private-mnzpytrl). Errors like AccessDenied or NoSuchBucket help confirm existence and access levels. Because bucket names are globally unique, teams often add a random or hashed suffix to avoid collisions. But if this suffix is reused, attackers can guess other related buckets more easily. To automate this process, tools like can search for cloud assets across AWS, Azure, and GCP:

Cloud are essential for automation and integration but also create a distinct attack surface. Even with authentication, misconfigured or overly permissive APIs can expose valuable information to attackers. CSPs typically offer two main access methods:

Tools like automate this by using roles instead of users, but the principle is the same: use IAM policy conditions to infer the account ID that owns the target bucket.

is an AWS post-exploitation tool for AWS that automates the enumeration using compromised credentials.

The AdministratorAccess policy is an AWS-managed policy that grants full access to all AWS services; its document is available in the .

Tools like and can map IAM relationships and effective access visually. In this case, it illustrates a clear privilege escalation path from admin-cbarton to admin-alice—a user with full administrative access.

cloud_enum
Application Programming Interfaces (APIs)
s3-account-search
Pacu
official AWS documentation
awspx
cloudmapper