参考:
https://exp10it.cn/2023/10/apache-activemq-%E7%89%88%E6%9C%AC-5.18.3-rce-%E5%88%86%E6%9E%90/
影响版本
ApacheMQ < 5.18.3
漏洞成因分析
这边的diff就比较简洁明了,可以看到漏洞点在BaseDataStreamMarshaller的createThrowable方法
在这里会进行任意类的有参实例化,而ApacheMQ是基于spring的,所以自然会有ClassPathXmlApplicationContext
这个类,我们让他进行实例化,即可完成RCE,但是必须就需要出网了。
正向分析
1 | package org.example; |
在org.apache.activemq.openwire.OpenWireFormat#doUnmarshal()
给上断点
一次ApacheMQ的通讯请求中会不断的有marshal和dounmarshal的操作。全局搜索一下
不难发现OpenWireFormat的marshall方法。回到doUnmarshal方法
会调用tightUnmarhsal或者looseUnmarshal方法,但其实都是一样的。回到漏洞点BaseDataStreamMarhsaller
其中ExceptionResponseMarshaller
是其子类。他内部有tightUnmarhsal方法
这个方法调用了tightUnmarsalThrowable
方法
而在这里面就有我们的漏洞点createThrowable。其中传入的参数是wireFormat
,所以OpenWireFormat就是对数据进行序列化和反序列化的类,我们需要在这里修改数据包。让clazz为恶意类,mesage为恶意参数。
这里Exception对应的是31,我们需要想办法readByte的值为31,因此我们往前翻序列化过程。this.wireFormat.marshal(command, this.dataOut);
我们在这里手动patch其中command为CPX类,我们的做法很简单,也只需要写一个相同的包名,这样由于方法调用机制,会优先寻找我们重写的类,然后调用其中的onway方法。
1 | public void oneway(Object command) throws IOException { |
1 | package org.springframework.context.support; |
我们patch两个类就行了。(注意重写getMedssage,这是为了让服务端获取到message,也就是实例化参数)继承Throwable是因为ExceptionResponse需要Exception类型,我们只是需要他的类名和传参。然后服务端会根据这2个值进行实例化。
可以看到序列化的时候果然进来了。也就是成功patch
逆向分析&&漏洞复现
环境搭建,咱们下载apachemq5.18.2之后怎么调试呢,它是基于wapper tomcat的,直接改一下conf文件就行了
1 | wrapper.java.additional.8=-Xdebug |
在wrapper.conf下添加如上内容即可。
然后开始debug,随后运行我们的POC,POC如上述正向分析所说
进入断点,注意,现在是服务端server的debug,上面是client客户端的debug
我们构造的恶意payload成功让databyte为31了,随之进入looseUnmarshal
方法内部。
然后进入漏洞点looseUnmarsalThrowable
Amazing,接着反射实例化完成RCE!
总结
还是得对一些产品的协议请求流程做一些了解,漏洞往往产生在这些通信过程中,最近见到的类似的漏洞已经够多了,X1roz师傅很厉害,我也得加把劲跟上大师傅们的脚步了。
About this Post
This post is written by Boogipop, licensed under CC BY-NC 4.0.