Lambda attacks
TL;DR: A Lambda function inherits its execution role’s credentials from a private metadata endpoint; RCE in the handler, malicious dependency, or write access to function code converts directly to those credentials and any over-broad IAM rights they hold.
What it is
Each Lambda invocation runs in a microVM that exposes the execution role’s STS credentials at http://169.254.79.129/2018-06-01/runtime/invocation/... or the AWS_CONTAINER_CREDENTIALS_FULL_URI env var, and mirrors them into AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN. Any code that runs inside the function — handler RCE via injection, malicious npm/PyPI dependency, attacker-supplied layer, or rewritten function code via UpdateFunctionCode — can read those creds and use them outside the function for the lifetime of the session.
Preconditions / where it applies
- Lambda handler with an input-controlled sink (SSRF, command injection, deserialisation) — or you can call
lambda:UpdateFunctionCode,lambda:UpdateFunctionConfiguration(env vars / layers), orlambda:PublishLayerVersion+ attach. - Or: dependency-confusion pathway into the function’s build.
- Execution role with rights worth stealing (S3, DynamoDB, KMS, STS chains).
Technique
Credential exfil from a compromised handler:
1
2
3
import os, urllib.request, json
print(os.environ["AWS_ACCESS_KEY_ID"], os.environ["AWS_SECRET_ACCESS_KEY"], os.environ["AWS_SESSION_TOKEN"])
# or from the runtime API for the per-invocation creds
Exfil to your own endpoint, then use the keys locally — they’re valid until the session expires (typically 12h).
Role takeover via UpdateFunctionCode: if your foothold principal has lambda:UpdateFunctionCode on a higher-privileged function, overwrite the zip with a one-liner that POSTs the env to you, then invoke (or wait for a trigger). The next execution runs as the target role.
1
2
3
zip -j x.zip handler.py
aws lambda update-function-code --function-name target --zip-file fileb://x.zip
aws lambda invoke --function-name target /dev/null
Env-var secret loot: many teams stuff DB passwords / API keys into Environment.Variables. lambda:GetFunctionConfiguration returns them in cleartext.
Layer poisoning: publish a layer that shadows a common module (e.g. requests), attach to a target function, and exfil on import.
Chains: handler RCE → STS creds → aws-iam-enum → aws-sts-assume-role hops → aws-secrets-manager dump.
Detection and defence
- CloudTrail: alert on
UpdateFunctionCode,UpdateFunctionConfiguration,PublishLayerVersionoutside CI principals. - Use Lambda code-signing (Signer) to reject unsigned zips.
- Scope execution roles narrowly — no
*resource on S3/DynamoDB; one role per function. - Move secrets from env vars into Secrets Manager / Parameter Store with KMS, fetched at runtime.
- GuardDuty
CredentialAccess:IAMUser/AnomalousBehaviorfires on Lambda role keys used outside Lambda VPC ranges. - Enable VPC egress controls so a compromised function cannot reach attacker-controlled endpoints.
References
- AWS — Lambda execution role — credential delivery model
- HackTricks Cloud — AWS Lambda privesc — known privesc paths
- Rhino Security — Lambda persistence — UpdateFunctionCode chains