一、漏洞背景
Airflow是一个可编程,调度和监控的工作流平台,它采用python语言编写,基于有向无环图(DAG),airflow可以定义一组有依赖的任务,按照依赖依次执行。airflow提供了丰富的命令行工具用于系统管控,而其web管理界面同样也可以方便的管控调度任务,并且对任务运行状态进行实时监控,方便了系统的运维和管理。
2020年7月14日,公布了CVE-2020-11978。按照cve mitre的描述,漏洞产生自Airflow自带的示例DAG中。
二、漏洞分析
example_trigger_target_dag代码
# -*- coding: utf-8 -*-
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import pprint
from airflow.models import DAG
from airflow.operators.bash_operator import BashOperator
from airflow.operators.python_operator import PythonOperator
from airflow.utils.dates import days_ago
pp = pprint.PrettyPrinter(indent=4)
# This example illustrates the use of the TriggerDagRunOperator. There are 2
# entities at work in this scenario:
# 1. The Controller DAG - the DAG that conditionally executes the trigger
# (in example_trigger_controller.py)
# 2. The Target DAG - DAG being triggered
#
# This example illustrates the following features :
# 1. A TriggerDagRunOperator that takes:
# a. A python callable that decides whether or not to trigger the Target DAG
# b. An optional params dict passed to the python callable to help in
# evaluating whether or not to trigger the Target DAG
# c. The id (name) of the Target DAG
# d. The python callable can add contextual info to the DagRun created by
# way of adding a Pickleable payload (e.g. dictionary of primitives). This
# state is then made available to the TargetDag
# 2. A Target DAG : c.f. example_trigger_target_dag.py
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(ds, **kwargs):
print("Remotely received value of {} for key=message". format(kwargs['dag_run'].conf['message']))
run_this = PythonOperator(
task_id='run_this',
provide_context=True,
python_callable=run_this_func,
dag=dag,
)
# You can also access the DagRun object in templates
bash_task = BashOperator(
task_id="bash_task",
bash_command='echo "Here is the message: '
'{{ dag_run.conf["message"] if dag_run else "" }}" ',
dag=dag,
)
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,
可以明显看到example_trigger_target_dag的代码中的
bash_command='echo "Here is the message: '
'{{ dag_run.conf["message"] if dag_run else "" }}" '
存在命令执行。也就是说,我们只要能够控制dag_run.conf的数据,就能实现命令注入。
而Airflow中,提供了一个Trigger DAG供开发者进行修改配置,也就是修改conf的模块。此时,我们便可以构造命令注入payload从而进行命令执行。
三、漏洞复现
我们需要构造语句进行命令绕过注入,重写conf构造形式如下
{"message":"'\";code;#"}
执行效果如下
四、影响版本
漏洞影响 Apache Airflow 小于1.10.11的版本
五、修复方式
1、 对管理界面添加身份认证
2、删除受影响的example_trigger_target_dag和example_trigger_controller_dag
3、升级Apache Airflow到1.10.11