ActiveMQ 的RCE 漏洞,直接从61616 端口默认协议上可触发,影响不小。
偷懒了当时只是验证了下没写分析,先在还得从头开始捡。
0x01 补丁分析
https://github.com/apache/activemq/commit/958330df26cf3d5cdb63905dc2c6882e98781d8f
分析出补丁位置不难,经典的org.springframework.context.support.ClassPathXmlApplicationContext 利用场景,以前出过几次,印象中最近的是浅蓝的postgresql 用的就是这个sink利用链。
0x02 漏洞分析
已知sink点为 BaseDataStreamMarshaller#createThrowable,路径在org.apache.activemq.openwire.v12
,其中openwire有不同版本,v开头,这里已v12 为例,往上回溯
1 | BaseDataStreamMarshaller#createThrowable |
其实跟踪到这里已经ok了,搭配上断点调试,已经可以定位出漏洞触发路径。
2.1 run->sink链
后续的触发路径还有很多,这里以tcp为例
1 | OpenWireFormat#unmarshal |
TcpTransport 是个Runable 类,run 是其接口,那么只要启动TcpTransport线程,那么到sink链就通了
2.2 source->start链
从source 继续往上推
1 | socket.getInputStream |
TransportThreadSupport#doStart
1 | protected void doStart() throws Exception { |
TcpTransport#doStart
1 | public class TcpTransport extends TransportThreadSupport implements Transport, Service, Runnable { |
2.3 start -> run
疑问:
Java thread#start 是在JNI实现上,实际调用的是run 接口,这在代码AST 上是无法追踪的,如何完善这一块?
0x03 漏洞复现
3.1 环境
1 | $ vim bin/env |
3.2 POC
1 | public class ExceptionResponseMarshallerPoc { |
0x04 横向拓展
4.1 其他协议
activemq 有其他协议,大家往往关注的是默认的61616 tcp协议,其实从原理分析过程来看,其他协议也存在问题。
例如udp 协议上也有问题:
不过最终的修复点是在sink点上,对newInstance 增加了过滤,因此这些路径也都被修复了。
4.2 安全手段
4.2.1 反序列化安全
4.2.1.1 InputStream
MessageDatabaseObjectInputStream
MessageDatabaseObjectInputStream
1 | private static class MessageDatabaseObjectInputStream extends ObjectInputStream { |
- 白名单模式
- 可能还有操作空间的只有xstream/activemq 这几个类
ClassLoadingAwareObjectInputStream
ClassLoadingAwareObjectInputStream.java
1 | public class ClassLoadingAwareObjectInputStream extends ObjectInputStream { |
1 | private void checkSecurity(Class clazz) throws ClassNotFoundException { |
SubSelectorClassObjectInputStream
SubQueueSelectorCacheBroker
1 | private static class SubSelectorClassObjectInputStream extends ObjectInputStream { |
4.2.1.2 Object
ActiveMQObjectMessage
ActiveMQObjectMessage.java
1 | public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMessage, TransientInitializer { |
1 | private Serializable deserialize(ByteSequence content) throws JMSException { |
1 | public void setTrustedPackages(List<String> trustedPackages) { |
ActiveMQStreamMessage
1 | public Object readObject() throws JMSException { |
1 | public Object readObject() throws JMSException { |
基本都封死了。
0x05 CodeQL回归验证
1 | /** |