Back to Cheat Sheets

🎭 Over-Privileged Function Permissions & Roles

OWASP Serverless Top 10 - SAS-4

HIGH RISK

📋 What Is It?

Over-Privileged Function Permissions & Roles occurs when a function is granted far more IAM permission than it needs. Instead of being scoped to the one table it reads or the one bucket it writes, the function carries wildcard actions, wildcard resources, broad *FullAccess managed policies, or a single shared role reused across the whole app. The permission the function holds is much larger than the permission it uses. In serverless there is no host or network to fall back on - the IAM policy is the security perimeter. Over-privilege is rarely the way in; it is the multiplier that turns a contained bug into an account-wide breach.

SAS-4 OWASP Rank
Blast Radius
Severe Impact

⚠️ Common Exploits

  • Shared Role: one broad provider-level role inherited by every function
  • Wildcard Actions: "*", s3:*, dynamodb:* - includes delete and full scans
  • Wildcard Resource: Resource: "*" turns a narrow action account-wide
  • Broad Managed Policies: *FullAccess attached "to unblock the build"
  • iam:PassRole: unscoped - borrow a more privileged role
  • iam:* Write: attach admin policy, self-escalate to account admin
  • Permission Drift: grants added per feature, never removed

🔴 Attack Flow

1. Get execution inside a function (injection / bad dep / leaked secret)
↓
2. Harvest the role's credentials from AWS_* env vars
↓
3. Enumerate what the role can do (List*, Get*, sts:GetCallerIdentity)
↓
4. Abuse wildcards: read every bucket/table, invoke every function
↓
5. IMPACT: PassRole / iam:* escalate to account-wide admin!

❌ Vulnerable Code

# Bad: one broad provider-level role inherited by ALL functions (serverless.yml) provider: name: aws iam: role: statements: - Effect: Allow Action: "*" ← every function can do everything Resource: "*" ← ...on every resource functions: createOrder: { handler: src/create.handler } # only needs PutItem on Orders healthCheck: { handler: src/health.handler } ← needs NOTHING, holds account-wide power // Bad: service wildcards and privilege-escalation permissions (JSON) { "Effect": "Allow", "Action": ["s3:*", "dynamodb:*"], // includes Delete*, full scans of every table "Resource": "*" } { "Effect": "Allow", "Action": ["iam:PassRole", "iam:AttachRolePolicy"], ← rewrite permissions "Resource": "*" } // unscoped PassRole = borrow AdminRole

✅ Secure Code

# Good: one least-privilege role PER function + a permission boundary ceiling plugins: - serverless-iam-roles-per-function provider: name: aws iam: role: permissionsBoundary: arn:aws:iam::111122223333:policy/FunctionBoundary # ceiling functions: createOrder: handler: src/create.handler iamRoleStatements: - Effect: Allow Action: dynamodb:PutItem # write-only Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/Orders getOrder: handler: src/read.handler iamRoleStatements: - Effect: Allow Action: dynamodb:GetItem # read-only, SEPARATE role Resource: arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/Orders healthCheck: handler: src/health.handler # no iamRoleStatements → logging only // Good: scoped actions, pinned ARN, tightened with a condition (JSON) { "Sid": "ReadIncomingUploadsOnly", "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::uploads-prod/incoming/*" } // If PassRole is truly needed, pin the role ARN + a service condition: { "Action": "iam:PassRole", "Resource": "arn:aws:iam::111122223333:role/OrdersWorker", "Condition": { "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" } } }

✓ Prevention Checklist

  • Give every function its own least-privilege role
  • Never attach a broad shared provider-level role
  • Replace every * with explicit actions and ARNs
  • Never use service:* or Resource: "*" for data services
  • Avoid *FullAccess managed policies
  • Tighten with conditions (SourceArn, LeadingKeys, encryption)
  • Scope iam:PassRole to specific role ARNs + service condition
  • Never grant iam:* or policy-editing actions to a function
  • Compare granted vs. used and remove the surplus
  • Apply permission boundaries as a hard ceiling
  • Separate roles by trust level and data sensitivity
  • Enforce least privilege in IaC and re-check every deploy

🔍 Detection & Tools

Least-Privilege Analysis:

IAM Access Analyzer Policy Generator (CloudTrail) Checkov cfn-nag iam-roles-per-function

Monitoring & Pivots:

CloudTrail CloudWatch Permission Boundaries Prisma Cloud Prowler

How to Test:

  • Inventory roles: is each function's role unique?
  • Grep IaC for Action: "*", service:*, Resource: "*"
  • Flag any *FullAccess managed policy on execution roles
  • Check for unscoped iam:PassRole and iam:*
  • Run Access Analyzer to compare granted vs. used
  • Simulate: from a function's role, what can you reach?

🌍 Real-World

These are documented incident classes - recurring patterns, not specific CVEs.

  • One compromised function, whole-account reach: an internet-facing function with s3:* and dynamodb:* on Resource: "*" is exploited; because the role is account-wide, the attacker exfiltrates buckets and tables unrelated to the function.
  • Privilege escalation via iam:PassRole: a limited role holds unscoped PassRole plus the ability to create compute; the attacker attaches a far more privileged existing role and operates as it.
  • *FullAccess as the default: a managed full-access policy attached in development and never tightened, exposing an entire service to any one weak function.
  • Shared-role blast radius: many functions share one role, so compromising the least-defended healthCheck yields the same power as every other function.
  • Denial-of-wallet via created compute: permission to spin up instances or functions abused for cryptomining on the victim's bill.

📌 Quick Tips

  • DO NOT share one broad role across functions
  • DO NOT use Action: "*" or Resource: "*"
  • DO NOT grant unscoped iam:PassRole or iam:*
  • DO give each function its own least-privilege role
  • DO pin specific actions and resource ARNs
  • DO cap roles with permission boundaries
  • DO compare granted vs. used continuously

📜 Compliance

Related Standards:

  • OWASP Serverless Top 10 - SAS-4
  • CWE-250 Execution with Unnecessary Privileges
  • CWE-269 Improper Privilege Management
  • PCI-DSS Requirement 7 - Least Privilege
  • SOC 2 / ISO 27001 Access Control