# AWS - Lambda Persistence {{#include ../../../../banners/hacktricks-training.md}} ## Lambda For more information check: {{#ref}} ../../aws-services/aws-lambda-enum.md {{#endref}} ### Lambda Layer Persistence It's possible to **introduce/backdoor a layer to execute arbitrary code** when the lambda is executed in a stealthy way: {{#ref}} aws-lambda-layers-persistence.md {{#endref}} ### Lambda Extension Persistence Abusing Lambda Layers it's also possible to abuse extensions and persist in the lambda but also steal and modify requests. {{#ref}} aws-abusing-lambda-extensions.md {{#endref}} ### Via resource policies It's possible to grant access to different lambda actions (such as invoke or update code) to external accounts:
### Versions, Aliases & Weights A Lambda can have **different versions** (with different code each version).\ Then, you can create **different aliases with different versions** of the lambda and set different weights to each.\ This way an attacker could create a **backdoored version 1** and a **version 2 with only the legit code** and **only execute the version 1 in 1%** of the requests to remain stealth.
### Version Backdoor + API Gateway 1. Copy the original code of the Lambda 2. **Create a new version backdooring** the original code (or just with malicious code). Publish and **deploy that version** to $LATEST 1. Call the API gateway related to the lambda to execute the code 3. **Create a new version with the original code**, Publish and deploy that **version** to $LATEST. 1. This will hide the backdoored code in a previous version 4. Go to the API Gateway and **create a new POST method** (or choose any other method) that will execute the backdoored version of the lambda: `arn:aws:lambda:us-east-1::function::1` 1. Note the final :1 of the arn **indicating the version of the function** (version 1 will be the backdoored one in this scenario). 5. Select the POST method created and in Actions select **`Deploy API`** 6. Now, when you **call the function via POST your Backdoor** will be invoked ### Cron/Event actuator The fact that you can make **lambda functions run when something happen or when some time pass** makes lambda a nice and common way to obtain persistence and avoid detection.\ Here you have some ideas to make your **presence in AWS more stealth by creating lambdas**. - Every time a new user is created lambda generates a new user key and send it to the attacker. - Every time a new role is created lambda gives assume role permissions to compromised users. - Every time new cloudtrail logs are generated, delete/alter them ### RCE abusing AWS_LAMBDA_EXEC_WRAPPER + Lambda Layers Abuse the environment variable `AWS_LAMBDA_EXEC_WRAPPER` to execute an attacker-controlled wrapper script before the runtime/handler starts. Deliver the wrapper via a Lambda Layer at `/opt/bin/htwrap`, set `AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap`, and then invoke the function. The wrapper runs inside the function runtime process, inherits the function execution role, and finally `exec`s the real runtime so the original handler still executes normally. {{#ref}} aws-lambda-exec-wrapper-persistence.md {{#endref}} ### AWS - Lambda Function URL Public Exposure Abuse Lambda asynchronous destinations together with the Recursion configuration to make a function continually re-invoke itself with no external scheduler (no EventBridge, cron, etc.). By default, Lambda terminates recursive loops, but setting the recursion config to Allow re-enables them. Destinations deliver on the service side for async invokes, so a single seed invoke creates a stealthy, code-free heartbeat/backdoor channel. Optionally throttle with reserved concurrency to keep noise low. {{#ref}} aws-lambda-async-self-loop-persistence.md {{#endref}} ### AWS - Lambda Alias-Scoped Resource Policy Backdoor Create a hidden Lambda version with attacker logic and scope a resource-based policy to that specific version (or alias) using the `--qualifier` parameter in `lambda add-permission`. Grant only `lambda:InvokeFunction` on `arn:aws:lambda:REGION:ACCT:function:FN:VERSION` to an attacker principal. Normal invocations via the function name or primary alias remain unaffected, while the attacker can directly invoke the backdoored version ARN. This is stealthier than exposing a Function URL and doesn’t change the primary traffic alias. {{#ref}} aws-lambda-alias-version-policy-backdoor.md {{#endref}} ### Freezing AWS Lambda Runtimes An attacker who has lambda:InvokeFunction, logs:FilterLogEvents, lambda:PutRuntimeManagementConfig, and lambda:GetRuntimeManagementConfig permissions can modify a function’s runtime management configuration. This attack is especially effective when the goal is to keep a Lambda function on a vulnerable runtime version or to preserve compatibility with malicious layers that might be incompatible with newer runtimes. The attacker modifies the runtime management configuration to pin the runtime version: ```bash # Invoke the function to generate runtime logs aws lambda invoke \ --function-name $TARGET_FN \ --payload '{}' \ --region us-east-1 /tmp/ping.json sleep 5 # Freeze automatic runtime updates on function update aws lambda put-runtime-management-config \ --function-name $TARGET_FN \ --update-runtime-on FunctionUpdate \ --region us-east-1 ``` Verify the applied configuration: ```bash aws lambda get-runtime-management-config \ --function-name $TARGET_FN \ --region us-east-1 ``` Optional: Pin to a specific runtime version ```bash # Extract Runtime Version ARN from INIT_START logs RUNTIME_ARN=$(aws logs filter-log-events \ --log-group-name /aws/lambda/$TARGET_FN \ --filter-pattern "INIT_START" \ --query 'events[0].message' \ --output text | grep -o 'Runtime Version ARN: [^,]*' | cut -d' ' -f4) ``` Pin to a specific runtime version: ```bash aws lambda put-runtime-management-config \ --function-name $TARGET_FN \ --update-runtime-on Manual \ --runtime-version-arn $RUNTIME_ARN \ --region us-east-1 ``` {{#include ../../../../banners/hacktricks-training.md}}