Lambda Function Pentest Findings That Repeat
Cloud Security
Serverless does not mean unattackable. Lambda functions leak through env vars, escalate through UpdateFunctionCode, and persist through poisoned layers. Here are the findings we see on every engagement.
By Arjun Raghavan, Security & Systems Lead, BIPI · February 13, 2025 · 7 min read
Lambda's threat model is not 'no servers, no problem.' It is a runtime that holds environment variables, an execution role, a deployment package, optional layers, and warm-execution state. Every one of those is attackable. A pentester treats Lambda as an identity plus a code update primitive plus a credential store.
How attackers find this
We list functions, dump configurations, and read environment variables (often the fastest path to a database password). We check execution role policies for the predictable escalations: AdministratorAccess attached because 'we will scope it later,' or s3:* across the account because debugging.
- Env var secrets: DB_PASSWORD, API_KEY in the function configuration; visible to anyone with lambda:GetFunctionConfiguration.
- lambda:UpdateFunctionCode on a function with a privileged role: replace the code, invoke, get role credentials.
- Layer poisoning: a function pulls a public layer; the publisher pushes a new version; next cold start runs attacker code.
- Dependency confusion in deployment packages: a private internal package name clashing with a public PyPI/npm name.
- /tmp persistence in warm executions: writes survive across invocations on the same micro-VM until reaped.
- Function URLs with AuthType: NONE: anyone on the internet can invoke.
Methodology in practice
We start with GetFunctionConfiguration on every function we can reach and grep environment variables for credential-looking strings. Then we look at the execution role for each function. A function with role AdminLambdaRole is a target; we trace whether our principal has UpdateFunctionCode on it. If yes, we replace the handler with a small reverse shell and invoke.
Detection
CloudTrail captures UpdateFunctionCode, UpdateFunctionConfiguration, PublishLayerVersion, and AddPermission. Detections worth writing: code updates outside CI principals (developers updating prod directly), AddPermission with Principal:* (public function), invoke patterns from new IPs against function URLs. CloudWatch logs from the function itself can be searched for outbound connection signatures if you log them.
Remediation
- Move secrets out of environment variables into Secrets Manager or Parameter Store; reference them at invocation time.
- Restrict lambda:UpdateFunctionCode to CI principals only; humans should not push code directly to production functions.
- Sign deployment packages with AWS Signer; configure the function with a code-signing config that rejects unsigned updates.
- Pin layer versions; never reference $LATEST on a layer you do not publish yourself.
- Run dependency-confusion checks in CI: scope private packages with a registry namespace and verify the resolution before publish.
- Treat /tmp as ephemeral; do not write secrets there, and assume warm-execution residue is observable.
- Disable Function URL public access unless explicitly required; if required, front it with API Gateway plus WAF.
Lambda's security posture is the sum of its identity, its code-update controls, and its dependency hygiene. Each of those is a checklist item. Functions that have all three are hard to attack; functions that have none are open doors.
Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.