提示: 靶场来自个人云服务器,真实网络环境渗透测试请严格遵守《中国网络信息安全法》,请勿轻易用于他人线上网络环境安全测试,本人不承担任何法律责任。
CVE-2023-37582之所以存在,是由于CVE-2023-33246的补丁中并未对DefaultRequestProcessor#updateConfig方法中的configStorePath属性值进行过滤,所以在低版本中,当NameServer地址暴露在公网并且缺乏权限校验,未经授权的攻击者可payload注入到 configStorePath中,调用NameServer的更新配置函数将恶意文件上传到RocketMQ服务器中实现远程代码执行。
下载安装包
我们简单汇总分析一下scheduler和shedlock的核心能力,以及我们融合的想法。
wget https://archive.apache.org/dist/rocketmq/4.9.6/rocketmq-all-4.9.6-bin-release.zip
broker.conf配置文件中都写成公网ip,便于测试。
公网开放端口(默认9876),至少开放nameserver端口:
然后使用root账户到rocketmq的bin目录执行启动命令:
#启动nameserver
nohup sh mqnamesrv >/dev/null 2>&1 &
#启动broker
nohup sh mqbroker -c /usr/local/rocketmq/rocketmq4.9.6/conf/broker.conf autoCreateTopicEnable=true >/dev/null 2>&1 &
之所以用root启动,是为了模拟最高权限启动的服务给渗透getshell成功后操作最危险的事情。
通过命令看到端口监听成功,服务启动成功:
通过nc命令,检查nameserver端口通畅。
编写python脚本:
此处我们是想要通过漏洞注入,添加crontab任务执行RCE,执行成功后会把所有的配置也都写到/var/spool/cron/root文件中,crontab会自动忽略不符合语法的任务,并且这种通过修改文件的方式添加,与crontab -e交互式添加任务不同的是,这种方式不需要重启crontab,而crontab -e交互式添加的任务需要重启crontab,也就是说我们只要写入到/var/spool/cron/root中并且格式合法,那么在下一个周期就会自动执行。
在执行python脚本之前,检查确认没有crontab任务:
上述添加的任务是每分钟执行在tmp目录创建success文件,确认该目录没有success文件。然后执行python脚本:
python3 exploit.py
没有报错,命令执行成功,然后到/var/spool/cron/看一下root文件:
和我们预期的一样,把配置内容都写到了root账户的调度配置文件中,上述内容除了*/1 * * * * touch /tmp/success其他全部非法,前边说过crontab会自动过滤,不再赘述。
我们再看一下任务有没有添加成功:
很明显,任务已经添加成功。等个一分钟到tmp目录看一下success文件有没有创建成功。
到这里还不算完,那倘若我在一台测试机上执行命令启动一个tcp服务:
nc -nvlp 4444
然后我把前边的python测试脚本稍作修改:
这样在执行python脚本会发生什么事情呢?很明显shell反弹,机器被控制。具体原理可自行研究。
CVE-2023-37582的本质是漏洞本质上是CVE-2023-33246的补丁绕过,看一下修复了什么东西:
修复的地方在namesrv,在updateconfig这个地方,这里可以用RocketMQ自己的协议,用不同的code调用过来实现不同的功能。
借别人抓取的一段tcp流量包分析:
提取tcp通信请求内容:
重点截取通信过程:
...c..._{"code":0,"flag":1,"language":"JAVA","opaque":0,"serializeTypeCurrentRPC":"JSON","version":435}...d...
协议主要包含四部分,协议总长度+json长度+json+body,前后两段的c、d对应的是ascii编码。
在上述json数据包当中,比较重要的是"code":25,不同的code代表了不同的业务,根据数据包当中的code字段,程序会进行不同的业务处理,对于nameserver,code是318:
处理业务在 org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest方法中。
我们以4.9.6为例分析,看一下上述processRequest方法:
然后进入updateConfig更新配置:
private RemotingCommand updateConfig(ChannelHandlerContext ctx, RemotingCommand request) {
final RemotingCommand response = RemotingCommand.createResponseCommand(null);
byte[] body = request.getBody();
if (body != null) {
String bodyStr;
try {
bodyStr = new String(body, MixAll.DEFAULT_CHARSET);
} catch (UnsupportedEncodingException e) {}
Properties properties = MixAll.string2Properties(bodyStr);
if (properties == null) {
log.error("updateConfig MixAll.string2Properties error {}", bodyStr);
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("string2Properties error");
return response;
}
if (properties.containsKey("kvConfigPath") || properties.containsKey("configStorePathName")) {
response.setCode(ResponseCode.NO_PERMISSION);
response.setRemark("Can not update config path");
return response;
}
this.namesrvController.getConfiguration().update(properties);
}
response.setCode(ResponseCode.SUCCESS);
response.setRemark(null);
return response;
}
这里把请求体重的属性转换成Properties然后更新,继续看 this.namesrvController.getConfiguration().update(properties)方法:
public void update(Properties properties) {
try {
readWriteLock.writeLock().lockInterruptibly();
try {
// the property must be exist when update
mergeIfExist(properties, this.allConfigs);
for (Object configObject : configObjectList) {
// not allConfigs to update...
MixAll.properties2Object(properties, configObject);
}
this.dataVersion.nextVersion();
} finally {
readWriteLock.writeLock().unlock();
}
} catch (InterruptedException e) {}
persist();
}
此处会从传入body解析的configStorePath替换掉默认的配置。
这里会遍历所有配置并更新配置,然后调用方法persist持久化:
public void persist() {
try {
readWriteLock.readLock().lockInterruptibly();
try {
String allConfigs = getAllConfigsInternal();
MixAll.string2File(allConfigs, getStorePath());
} catch (IOException e) {
log.error("persist string2File error, ", e);
} finally {
readWriteLock.readLock().unlock();
}
} catch (InterruptedException e) {}
}
此方法的目的其实是将configObjectList写入进对应的配置文件当中。
其实这个漏洞的本质就是从传入的configStorePath替换了默认的路径并实现了任意文件写,导致的远程RCE。
从漏洞库搜索到rocketmq还有挺多其他漏洞,有很多人吐槽大厂不用的东西开源出来坑别人,其实不然,对于工具类开源项目,其他的大厂人家也有能力自研,就算用他的也会开源+二开,然后主要是中小厂图简单省事儿用它,这些企业和项目对安全、运维的投入相对较少,专业度相对较弱,也基本没有内网环境,所以公网裸奔,之前内网运行的东西,放到公网环境运行,可以想象会有多少bug和漏洞。另外它bug和漏洞多,不代表其他中间件漏洞少,比如一个中间件10个人用,用了一年发现一个漏洞,另外一个中间件 10000个人用,可能每天都能发现bug。
所以对于开源软件,不要排斥,也不要一股脑地接受,理性看待,对于一两个人维护的项目你用它干干啥,复杂一些、规模大一些的项目,选择中间件也是个相对慎重的过程,并且也会有专人专项时刻观察市面上各大漏洞库的公布内容,一旦发现有严重bug那么就及时修复或者升级。
另外对于本篇内容,如果感兴趣的,可以一起沟通交流,如果需要工具或者上述脚本的可以自行搜索或者联系作者,最后还要强调一下,不要去尝试访问非自己持有的机器。
https://github.com/apache/rocketmq/pull/6843
https://lists.apache.org/thread/m614czxtpvlztd7mfgcs2xcsg36rdbnc
https://github.com/Malayke/CVE-2023-37582_EXPLOIT
https://drun1baby.top/2023/11/21/CVE-2023-37582-Apache-RocketMQ-RCE-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
https://avd.aliyun.com/detail?id=AVD-2023-37582
https://www.cnblogs.com/spmonkey/p/17767846.html
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!