mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-12 15:50:19 -08:00
144 lines
6.2 KiB
Markdown
144 lines
6.2 KiB
Markdown
# 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}}
|
||
|
||
|
||
|
||
|