CVE-2023-37582
产品介绍
RocketMQ是阿里巴巴在2012年开发的分布式消息中间件,后捐献给Apache软件基金会并成为Apache的顶级项目。RocketMQ专为万亿级超大规模的消息处理而设计,具有高吞吐量、低延迟、海量堆积、顺序收发等特点。
漏洞概述
当RocketMQ的NameServer组件暴露在外网,并且缺乏有效的身份认证机制时,攻击者可以利用nameServer更新配置功能,以RocketMQ运行的系统用户身份进行任意文件上传,甚至实现远程代码执行。
受影响版本
<RocketMQ 4.9.7
<RocketMQ 5.1.2
漏洞分析
RocketMQ基本概念
要想对该漏洞有较为清晰的认识,我们首先要了解RocketMQ的基本架构以及各部分的功能。
RocketMQ主要由四部分组成:
概念 | 说明 |
---|---|
Producer | Prducer负责消息的生产,支持分布式集群部署,通过MQ的负载均衡系统选择相应的Broker集群进行消息的存储与投递。 |
Consumer | Consumer负责消息的消费,支持分布式集群部署,同时支持通过PUSH与PULL的方式对消息进行消费。 |
NameServer | NameServer是一个Topic路由注册中心,其角色类似于Dubbo中的ZooKeeper,Hadoop中的NameNode,负责整个系统元数据的存储与管理,支持Broker的动态注册与发现。其主要包含两个功能。一是Broker的注册与管理,其接受Broker的信息并保存作为路由的基本信息,并提供心跳机制来确保每一个Broker集群的有效性;二是路由信息的管理,NameServer存储了整个系统的Broker路由信息,Producer与Consumer通过NameSerVer便可进行消息的投递与消费。 |
Broker | Broker主要负责消息的存储、投递与消费并保证服务的高可用。 |
下图为各组件的关联方式:
测试环境搭建
调试环境
访问项目官方仓库获取受影响漏洞版本源码(本文使用v4.9.4版本)
1 | https://github.com/apache/rocketmq/releases |
使用idea导入项目后需启动NameServer
与Broker
,同时为BrokerStarup
与NamesrvStartup
配置运行时环境变量ROCKETMQ_HOME
,两者配置相同地址即可,为了让Broker
能够找到Namesrv
,Broker
的配置中需要额外指定命令行参数-c namesrv_endpoint
ROCKETMQ_HOME
为ROCKETMQ运行家目录,配置好环境变量后在ROCKETMQ_HOME
指向的目录行新建文件夹conf
,进入conf
目录并新建文件logback_broker.xml
与logback_namesrv.xml
,将以下内容复制到文件中:
1 |
|
环境配置完成后运行主类org.apache.rocketmq.broker.NameSrvStartup
启动ROCKETMQ NameServer
,控制台输出以下内容则NameServer
启动成功
运行主类org.apache.rocketmq.broker.BrokerStartup
启动ROCKETMQ Broker
,控制台输出以下内容则Broker
启动成功
日志中提示了Broker
监听地址与NameServer
地址。
靶场搭建
调试环境可用于测试PoC
详情
为了更清晰地了解该漏洞,我们仍需了解NameServer
的启动过程。另一方面,该漏洞是针对NameServer
的攻击与Broker
关系不大,了解NameServer
的启动过程将更有助于我们分析漏洞。
在org.apache.rocketmq.namesrv.NamesrvStartup
类中,启动阶段的关键代码出现在第57与58行。启动时首先要做的是创建NameSrvController
进行初始化,然后再执行start
流程。
创建Controller
过程中主要进行了命令行参数解析、配置文件解析、日志配置解析并将相关的配置映射到注册namesrvConfig
与nettyServerConfig
两个对象中并向Controller
注册
start
启动过程中最终会调用到org.apache.rocketmq.remoting.netty.NettyRemotingServer
的start
方法
该方法主要完成了各类处理器的初始化、Netty解码器与消息处理器的注册、监听服务的启动。
其中encoder
负责RocketMQ
协议消息头的解析与消息体的提取,最终形成字节流传递给serverHandler
进行处理。
serverHandler
会根据不同的消息类型选择不同的处理方式
在进行Request
类型消息处理时会先选择处理器类型,然后调用其processRequest
方法进行具体的消息处理
默认使用的消息处理器类为org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor
,其processRequest
方法会根据请求中的code
参数选择不同的处理方式。
在监听服务启动后,netty
将按照每秒1次的频率不断获取监听端口信息,然后一次调用decoder
、handle
r对消息进行处理,上面就是Namesrv
的简单启动流程。
有趣的是当org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest
第88行request.getCode
方法的结果为318
时则进入到配置更新流程
在updateConfig
方法中首先会获取请求体对象然后转换为字符串,然后将字符串转换为properties对象,最终将properties更新到configuration对象中
this.namesrvController.getConfiguration()
获取到的是一个Configuration对象,然后调用其update
方法进行配置更新
org.apache.rocketmq.common.Configuration#update
方法首先将用户请求体的配置信息合并到this.allconfigs
中,然后遍历configObjectList
对象成员,再将配置信息合并到其中。从上图我们知道configObjectList
包含两个对象分别是程序启动过程中对configuration
初始化时传入的NamesrvConfig
与NettyServerConfig
对象。完成配置在内存态的更新后调用persist
方法对配置进行持久化。
persist
方法完成了在内存中实时的配置信息的持久化,其首先会调用getAllConfigsInternal
方法获取所有配置,然后调用getStorePath
方法获取到配置的存储路径,最终调用string2File
方法以前两个要素为参数配置持久化到硬盘。
一般来讲,任意文件上传漏洞需要满足两个条件,一是文件的后缀名可控,二是文件的内容可控。对于该漏洞来说我们通过getAllConfigsInternal
获取到文件内容,通过getStorePath
获取到文件路径。那么这两者是否可控呢?
我们来看getAllConfigsInternal
方法,该方法主要做了两件事,一是遍历configObjectList
对象然后将其成员转换为properties
对象,二是将properties
对象序列化为字符串。从前面我们知道configObjectList
对象可以被用户通过code
为318
的请求修改,也就是说文件内容是可控的。
再看getStorePath
方法,在第156
行有很明显的反射调用,获取的值是NamesrvConfig
对象中configStorePath
字段的值。
在update
方法中185行我们知道,用户请求体中的配置会修改NamesrvConfig
的属性,故持久化的配置文件的存储路径也是可变的,于是便构成了任意文件上传的两个要素。
我们使用互联网流传的PoC对漏洞进行验证,因为本文的复现环境为Windows所以需要对PoC进行适当的修改,可以看到在PoC中,用户发送给NameServer
的请求体数据为两个通过换行符分割的键值对类型数据,分别为configStorePath
与productEnvName
,其中configStorePath
负责指定配置文件的存储路径,productEnvName
只是用于验证配置是否写入成功。
执行验证脚本
到启动目录查看发现文件写入成功
若需要进行命令执行只需要向启动目录里写入bat文件即可
或者向.ssh文件夹下的authorized_keys中写入新的公钥(未验证)
最后放上调用栈
1 | persist:208, Configuration (org.apache.rocketmq.common) |
修复措施
1、非必须勿向互联网开放不必要的服务端口,控制暴露面;
2、关键服务增加用户权限校验;
3、升级软件到不受影响的版本或最新版。
参考链接
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-37582
- https://www.cvedetails.com/cve-details.php?t=1&cve_id=CVE-2023-37582
- https://www.cnnvd.org.cn/home/globalSearch?keyword=CVE-2023-37582