前言

RocketMQ RCE(CVE-2023-33246)已经爆出挺久了,不过官方的描述比较迷惑导致刚开始关注度比较小,不过也能理解官方的通告,下面来分析一下这个漏洞,作个记录~

漏洞分析

协议分析

随便打入一段请求
image-20230603173337368
经过部分调试,发现协议主要包含四部分
协议总长度+json长度+json+body
构造的python脚本如下

import socket
import binascii
client = socket.socket()
# you ip
client.connect(('192.168.111.129',10911))
# data
json = '{"code":25,"extFields":{"test":"RockedtMQ"},"flag":0,"language":"JAVA","opaque":266,"serializeTypeCurrentRPC":"JSON","version":433}'.encode('utf-8')
body='filterServerNums=1\nnamesrvAddr=127.0.0.1:9876\nrocketmqHome=1'.encode('utf-8')
json_lens = int(len(binascii.hexlify(json).decode('utf-8'))/2)
head1 = '00000000'+str(hex(json_lens))[2:]
all_lens = int(4+len(binascii.hexlify(body).decode('utf-8'))/2+json_lens)
head2 = '00000000'+str(hex(all_lens))[2:]
data = head2[-8:]+head1[-8:]+binascii.hexlify(json).decode('utf-8')+binascii.hexlify(body).decode('utf-8')
# send
client.send(bytes.fromhex(data))
data_recv = client.recv(1024)
print(data_recv)

其中code代表调用的不同功能
image-20230603173702165
之后就可以愉快的调用各种功能了。

漏洞分析——任意文件写入

这个功能是出现在UPDATE_NAMESRV_CONFIG
image-20230603165156534
解析传入的body并且写入配置
首先是配置更新,这一步不用管,只要是org.apache.rocketmq.common.namesrv.NamesrvConfig 中的字段,都能更新,重点是persist功能,我们看到有一个文件写入的调用
image-20230603165235099
会写入fileName、fileName.bak两个文件,这里的fileName为configStorePath,也就是后来黑名单中的字段
image-20230603165412365
既然可以任意文件写入,所有就可以是常规的一些RCE思路,比如写入计划任务。
1、修改存储路径为计划任务路径,并且打入定时任务脚本,注意可以使用\\n 来进行换行操作。
image-20230603165620594
2、成功写入
image-20230603165717786
3、命令执行成功
image-20230603165728998
这里就需要条件了,比如root权限,但是我们可以通过 GET_NAMESRV_CONFIG 功能来获取当前用户权限。
初始化的kvConfigPath、configStorePath带有当前用户
image-20230603165844124
image-20230603170025365
这里就是漏洞通告中的在一定条件下,还是挺具有迷惑性的,这个功能影响三个模块,NameServer, Broker, Controller,通过加入黑名单修复

漏洞分析——命令执行

这个漏洞还是挺不好发现的,也就是漏洞的sink不好发现,在没有了解RocketMQ下,还挺不好找。
在broker 一堆的功能中,有个REGISTER_FILTER_SERVER 引起我的注意,
image-20230603170604318
当然这里跟进一圈也就是注册功能,没有什么危害,但是我比较好奇这部分如何实现,凭感觉这里可能会出现漏洞,哈哈
随便翻翻,发现各种名词,过滤器类、注册呀什么的,感觉妥妥的危险操作,直接进入 org.apache.rocketmq.broker.filtersrv.FilterServerManager 查看下这个模块把
很容易就看到了有个命令拼接的地方
image-20230603171840151
仔细一看,这里拼入的参数来自brokerConfig,而通过之前的分析,我已经知道brokerConfig可以被控制了,现在就要看这个功能怎么触发,
跟进发现,这里是新建了一个线程,每隔一段时间就触发
image-20230603172054407
在启动Broker的时候,调用startBasicService
image-20230603172214497
进行了调用。
image-20230603172247365
一个RCE漏洞便出现了,其实这里没有调试,在熟悉流程后,点几下就可以发现这个漏洞。
所以我们打入我们的命令执行POC
这里刚开始还测了一段时间,怎么也触发不了(Windows 下压根不行)后来改用
sh -c touch 命令
要执行的命令间隔用\t 这样就不会被拆分了。
image-20230603172528709
查看日志,可以看到命令执行记录
image-20230603172538784
修复方案十分粗暴,直接把功能删了,本来还想再研究下的。

后言

这个漏洞的发现还是有一定巧合的,这类中间件大多以非阻塞方式开发,涉及到很多线程,也就导致漏洞并不好追踪,功能也不好阅读,最好的方式还是查阅文档,并且阅读前人的源码分析、架构分析,这样可以节约大量时间。至于官方的在一定条件下,可能就是在说第一个漏洞了,最后两个漏洞给了一个编号,这也就导致漏洞的描述比较迷惑,hh~