0x00、前言
Apache-RocketMQ远程代码执行漏洞(CVE-2023-37582)原理分析,该漏洞为历史漏洞(CVE-2023-33246)补丁未修复完全,导致可以向nameserver发送更新配置信息从而写入文件。
回顾上个月的历史漏洞(CVE-2023-33246)原理:
由于RocketMq未对不同角色访问做严格的身份验证,攻击者可伪造成RocketMq管理端向服务器的broker(代理服务器)发送更新Broker配置更新请求,请求中包含了恶意代码,broker server接收到更新请求会调用callshell导致恶意代码执行。
0x01、漏洞描述
Apache RocketMQ是一款开源的分布式消息和流处理平台,提供了高效、可靠、可扩展的低延迟消息和流数据处理能力,广泛用于异步通信、应用解耦、系统集成以及大数据、实时计算等场景。
Apache RocketMQ远程代码执行漏洞(CVE-2023-37582)细节及POC已公开,由于历史漏洞(CVE-2023-33246)未修复完全,最终可以RocketMQ运行的系统用户身份执行任意命令。
0x02、漏洞范围
Apache RocketMQ <= 5.1.1
0x03、环境搭建
这里直接拉取docker镜像
docker pull apache/rocketmq:5.1.1
启动nameserver
docker run -it --net=host apache/rocketmq:5.1.1 ./mqnamesrv
idea调试:
进入docker容器,修改启动配置文件(runbroker.sh、runserver.sh)
添加idea debug jvm远程调试的代码
然后重启docker容器即可
docker restart <ContainerId>
idea开启debug即可调试
(如果debug连接出现握手失败,换个调试端口)
0x04、漏洞复现
已公开对应的检测exp:
0x05、原理分析
RocketMQ-NameServer源码可参考文章,文章中对nameserver的启动、功能、执行流程有很清晰的描述
引用一张上文中nameserver的时序流程图
可以看到nameserver通过NamesrvStartup
类进行启动,启动的源码很好跟进,不多叙述,重点在接收请求的处理类DefaultRequestProcessor
也就是图中的步骤10
通过建立连接,获取请求的code
值,通过code
值的的不同进入不同的处理分支。
当code
值为UPDATE_NAMESRV_CONFIG
,也就是318
时,会调用updateConfig
方法用于更新配置
updateConfig
方法会获取请求体的内容,将请求体内容转化为字符串
将请求体配置内容存放进环境配置变量properties
中,随后判断如果配置内容key中存在kvConfigPath
和configStorePathName
,则直接返回,无法更新配置
该黑名单匹配为历史漏洞(CVE-2023-33246)补丁修复添加的,为了杜绝可能存在风险的更新配置功能点,在一些控制管理和角色组件中添加了黑名单,与此同时还添加了其它的黑名单匹配来防止更新配置文件
在nameserver角色组件中,过滤了上图中的kvConfigPath
和configStorePathName
字段,但是没有过滤configStorePath
字段,导致仍可使用configStorePath
字段进行更新配置文件,导致的补丁绕过,接着往下跟进
在获取到配置内容后,调用update
更新
通过mergeIfExist
方法将properties
信息添加到allConfigs
中,前提是properties
中的key值必须在allConfigs
hash表中存在才能覆盖表中的原有值
private void mergeIfExist(Properties from, Properties to) { |
添加过后,allconfigs
包含了所有的配置信息包括我们请求体中的配置信息
接下来需要找到allconfigs
配置信息中可控的元素,来实现对文件内容的可控,这里找到了配置变量productEnvName
,通过productEnvName
变量的可控,实现对写入内容的可控,由于已经运行过payload,因此value值为写入的测试数据。
原理上只要是allconfigs
hash表中的字符串类型元素,且不影响下面反射赋值后对写入产生影响的元素均可以作为文件内容的可控元素。
但此时,并未在系统中更改对应configStorePath
配置属性的值,只是将请求体信息添加更新到allconfigs
中,作为写入的内容而已
接着调用properties2Object
方法,通过反射调用存在set
前缀的属性方法,这里通过调用setconfigStorePath
方法对configStorePath
字段进行赋值,实现对文件写入路径的可控
接着调用persist
方法
获取到allconfigs
信息后,调用MixAll.string2File
方法allconfigs
信息将写入指定的configStorePath
目录文件中,深入利用还可以将写入路径改为linux计划任务中,实现自启动执行命令
路径通过反射获取configStorePath
的值
最后将allconfigs
配置信息写入configStorePath
路径文件中
payload的数据包,configStorePath
路径和productEnvName
内容可控,可实现任意文件的写入
0x06、漏洞修复
目前官方已发布安全版本,建议受影响用户升级至:
RocketMQ 5.x >= 5.1.2
RocketMQ 4.x >= 4.9.7
官方补丁下载地址:
https://rocketmq.apache.org/download/
修复方式:
- 黑名单匹配添加configStorePath字段,用户请求中包括configStorePath字段将无法进行配置更新请求
0x07、参考链接
https://github.com/apache/rocketmq/commit/9d411cf04a695e7a3f41036e8377b0aa544d754d
https://blog.51cto.com/search/user?uid=14281117&q=rocketmq
https://github.com/apache/rocketmq/commit/c1fdf1d62c627d6cfbae06d0e15f1c23c7be654b