# 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:

<figure><img src="../../../../images/image (255).png" alt=""><figcaption></figcaption></figure>

### 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.

<figure><img src="../../../../images/image (120).png" alt=""><figcaption></figcaption></figure>

### 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:<acc_id>:function:<func_name>: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}}




