Files
hacktricks-cloud/src/pentesting-cloud/aws-security/aws-persistence/aws-lambda-persistence/README.md
JaimePolop e4e6a409ce update
2025-10-22 23:26:58 +02:00

144 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 doesnt 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 functions 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}}