漏洞分析|Adobe ColdFusion WDDX 序列化漏洞利用

news2025/1/19 20:19:48

0x01 概述

在上一篇有关 Adobe ColdFusion 序列化漏洞(CVE-2023-29300)的文章中,我们对已公开的 JNDI 利用链(CVE-2023-38204)进行了复现。JNDI 利用链受目标出网的限制,在不出网的情况下无法很好地利用。本文中我们将分享不出网的利用方式,提出 C3P0 和 JGroups 两条基于服务错误部署的新利用链。经过测试,C3P0 存在被利用的可能,JGroups 利用成功率为 0.1%。尽管能被利用成功的概率较低,我们也已经在 Goby 中实现了 C3P0 和 JGroups 利用链的完整利用,完全支持命令执行以及结果回显功能。

0x02 Apache Felix

ColdFusion 通过自身实现的FelixClassloader来调用BundleClassLoader#loadClass()方法,用于启动和禁用后台一个叫做 Package Manager 的服务下载的插件,动态加载 bundles 文件夹下的各种 Jar 包。
我们可以看到这些 Jar 包的 MANIFEST 文件中多了不少字段:

Manifest-Version: 1.0
Bnd-LastModified: 1490514990031
Build-Jdk: 1.8.0_111
Built-By: uriel
Bundle-Description: Java reflect give poor performance on getter sette
 r an constructor calls, accessors-smart use ASM to speed up those cal
 ls.
Bundle-DocURL: http://www.minidev.net/
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-ManifestVersion: 2
Bundle-Name: accessors-smart
Bundle-SymbolicName: net.minidev.accessors-smart
Bundle-Vendor: Chemouni Uriel
Bundle-Version: 1.2
Created-By: Apache Maven Bundle Plugin
Export-Package: net.minidev.asm;version="1.2.0";uses:="org.objectweb.a
 sm",net.minidev.asm.ex;version="1.2.0"
Import-Package: net.minidev.asm.ex;version="[1.2,2)",org.objectweb.asm
 ;version="[5.0,6)"
Tool: Bnd-3.3.0.201609221906

搜索可知,Apache Felix 是 OSGi(Open Service Gateway Initiative,开放服务网关协议)的一种开源实现框架,通过为 Jar 包添加 metadata 来定义哪些类暴露,哪些类隐藏,来实现依赖的模块化,其控制单元就叫做 bundle。
其中最重要的就是Export-PackageImport-Package两个字段,分别用于告诉 OSGi 自己对外提供哪些类,引用了其它包的哪些类。如果没有这两个字段,OSGi 或者说它的实现 Felix 就无法正常工作。

0x03 构造利用链

3.1 Felix ClassLoader

ColdFusion 在进行 WDDX 序列化的过程中,将调用BundleClassLoader来对目标类进行加载。实现的findClass()方法如下:

protected Class findClass(String name) throws ClassNotFoundException {
    Class clazz = this.findLoadedClass(name);
    if (clazz == null) {
        // ...

        String actual = name.replace('.', '/') + ".class";
        byte[] bytes = null;
        List<Content> contentPath = this.m_wiring.m_revision.getContentPath();
        Content content = null;

        for(int i = 0; bytes == null && i < contentPath.size(); ++i) {
            bytes = ((Content)contentPath.get(i)).getEntryAsBytes(actual);
            content = (Content)contentPath.get(i);
        }

        if (bytes != null) {
            String pkgName = Util.getClassPackage(name);
            Felix felix = this.m_wiring.m_revision.getBundle().getFramework();
            Set<ServiceReference<WeavingHook>> hooks = felix.getHookRegistry().getHooks(WeavingHook.class);
            Set<ServiceReference<WovenClassListener>> wovenClassListeners = felix.getHookRegistry().getHooks(WovenClassListener.class);
            WovenClassImpl wci = null;
            // ...

            try {
                clazz = this.isParallel() ? this.defineClassParallel(name, felix, wovenClassListeners, wci, bytes, content, pkgName) : this.defineClassNotParallel(name, felix, wovenClassListeners, wci, bytes, content, pkgName);
            } catch (ClassFormatError var17) {
                // ...
            }

            // ...
        }
    }

    return clazz;
}

此处会将全限类名转换为相对文件路径,如传入javax.sql.ConnectionPoolDataSource,则会尝试读取相对路径javax/sql/ConnectionPoolDataSource.class文件中的字节码,之后再去defineClassParallel()方法中调用defineClass()。由于这种特殊的类加载模式,即使ConnectionPoolDataSource存于rt.jar,也会因为无法定位 class 文件而抛出NoClassDefFoundError

java.lang.NoClassDefFoundError: javax/sql/ConnectionPoolDataSource
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClass(BundleWiringImpl.java:2338)
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClassParallel(BundleWiringImpl.java:2156)
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2090)
	at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1556)
	at org.apache.felix.framework.BundleWiringImpl.access$300(BundleWiringImpl.java:79)
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1976)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
...

不过还有一种可能,如果目标类已提前被加载,ClassLoader#findLoadedClass()就会直接返回目标类,也不会进入抛出错误的分支。那么会不会有人部署服务时不走寻常路,将原本散落各处的依赖包全部集中到了自定义的 lib 中呢?
本着遵循客观事实的原则,我们进行了利用测试,结果表明的确存在可以利用的目标:
image.png
image.png
无一例外,这些目标都手动更改了服务的依赖包路径。相比使用 Package Manager 服务,他们更愿意自己手动管理依赖,这也将绕过BundleClassLoader的依赖加载机制,留给我们相当大的操作空间。

3.2 C3P0

WrapperConnectionPoolDataSourceBase#setUserOverridesAsString()方法为入口,构造的调用链如下,最后将传入的字节解码并调用原生反序列化。

WddxDeserializer
	-> deserialize()

...

WrapperConnectionPoolDataSourceBase
	-> setUserOverridesAsString()

VetoableChangeSupport
	-> fireVetoableChange()

VetoableChangeListener
	-> vetoableChange()

C3P0ImplUtils
	-> parseUserOverridesAsString()

SerializableUtils
	-> fromByteArray()
	-> deserializeFromByteArray()

ObjectInputStream
	-> readObject()

parseUserOverridesAsString()方法将截取并 Hex 解码传入的字节流,因此我们构造原生序列化流时需要相应地填写占位和进行编码。

    public static Map parseUserOverridesAsString(String userOverridesAsString) throws IOException, ClassNotFoundException {
        if (userOverridesAsString != null) {
            String hexAscii = userOverridesAsString.substring("HexAsciiSerializedMap".length() + 1, userOverridesAsString.length() - 1);
            byte[] serBytes = ByteUtils.fromHexAscii(hexAscii);
            return Collections.unmodifiableMap((Map)SerializableUtils.fromByteArray(serBytes));
        }
        // ...
    }

_5db20be14d804ed14721d4d90a25560c_-1996400489_图片.gif

3.3 JGroups

和 C3P0 类似,JGroups 的ReplicatedTree#setState()方法接受字节数组参数,并调用原生反序列化,利用链如下。

WddxDeserializer
	-> deserialize()

...

ReplicatedTree
	-> setState()

Util
	-> objectFromByteBuffer()
	-> oldObjectFromByteBuffer()

ObjectInputStream
	-> readObject()

由于setState()方法接收的参数不是基本类型,我们需要利用<binary>标签触发BinaryHandler来传入字节数组,并进行 base64 编码。

public void onEndElement() throws WddxDeserializationException {
	this.setValue(Base64Encoder.decode(this.m_buffer.toString()));
	this.setType(-3, "VARBINARY");
}

objectFromByteBuffer()方法将根据传入的第一个字节,调用不同的readObject(),因此构造原生序列化流时还需要在首位添加一个0x2

public static Object objectFromByteBuffer(byte[] buffer, int offset, int length) throws Exception {
    // ...
    ByteArrayInputStream in_stream = new ByteArrayInputStream(buffer, offset, length);
    byte b = (byte)in_stream.read();
    // ...
    try {
        ObjectInputStream ois;
        switch (b) {
            // ...
            case 2:
                in = new ObjectInputStream(in_stream);
                retval = ((ObjectInputStream)in).readObject();
                break;
            // ...
        }
        // ...
    }
    // ...
}

aaa.jpg_a5b5e3b901682635665925c370e2e751_-696553035_图片(1).gif

0x04 总结

通过深入挖掘 CVE-2023-29300 的利用方式,我们摸清了产品更多的细节,如BundleClassLoader的类加载机制,并最终提出了两条不出网的利用链:C3P0 和 JGroups。虽然在默认部署环境中无法成功利用,我们最开始也不相信有人会手动破坏依赖管理机制,但事实是规则总有人打破,而这也让攻击者得以趁虚而入。
希望在阅读本篇文章后,能为大家带来一些新思路的启发。

0x05 参考链接

https://helpx.adobe.com/security.html
https://felix.apache.org/documentation/index.html


Goby 欢迎表哥/表姐们加入我们的社区大家庭,一起交流技术、生活趣事、奇闻八卦,结交无数白帽好友。

也欢迎投稿到 Goby(Goby 介绍/扫描/口令爆破/漏洞利用/插件开发/ PoC 编写/ IP 库使用场景/ Webshell /漏洞分析 等文章均可),审核通过后可奖励 Goby 红队版,快来加入微信群体验吧~~~

文章来自Goby社区成员:M1sery@白帽汇安全研究院,转载请注明出处。
微信群:公众号发暗号“加群”,参与积分商城、抽奖等众多有趣的活动
获取版本:https://gobysec.net/sale

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树的递归遍历和非递归遍历

目录 一.二叉树的递归遍历 1.先序遍历二叉树 2.中序遍历二叉树 3.后序遍历二叉树 二.非递归遍历(栈) 1.先序遍历 2.中序遍历 3.后序遍历 一.二叉树的递归遍历 定义二叉树 #其中TElemType可以是int或者是char,根据要求自定 typedef struct BiNode{TElemType data;stru…

核心实验11合集_hybrid接口特殊用法_ENSP

项目场景一&#xff1a; 核心实验11合集_hybrid接口特殊用法_ENSP 前期用户少&#xff0c;只有一个vlan段&#xff0c;如今需要划分不同vlan&#xff0c;使用hybrid接口实现。&#xff08;不可更改ip地址&#xff09; 实搭拓扑图&#xff1a; 具体操作&#xff1a; sw1: [sw1…

​Bigemap软件在农业行业中的应用

​Bigemap软件在农业上面的一些应用 在农业工作者平时的工作应用中 Bigemap可以帮助农业用户更好地管理土地、作物和水资源 &#xff1b;提高农业生产效率和质量 &#xff1b;以及 野外调查定位&#xff0c;地层分析论证&#xff0c;水文地质调查等&#xff0c; 大部分的农业…

TypeScript断言

什么是断言&#xff1f; 一个编译时语法&#xff0c;用于告诉编译器用户比编译器更加确定变量的类型&#xff0c;进而解除编译错误&#xff0c;类型断言有点类似于其他语言的类型转换&#xff0c;但它没有运行时的影响&#xff0c;只是在编译阶段起作用。所以&#xff0c;即使通…

typescript删除array中的空值

使用.flat() 可以看到&#xff0c;调用之后空值被清清除了&#xff0c;如果本身就是1维数组就无所谓&#xff0c;但如果本身是多维数组&#xff0c;又不想数组维度被改变的话就需要传入0&#xff0c;才不会导致数据维度改变

如何启动股票量化系统QTYX-Python3.7/3.9环境安装Anaconda+Pycharm及TaLib

前言 我们的股票量化分析系统QTYX提供两种形式使用&#xff1a; EXE安装文件。好处是不需要安装Python环境&#xff0c;双击安装EXE文件就能使用QTYX的功能。Python源码。好处是可以学习和调试源码&#xff0c;并且在此基础上二次开发&#xff0c;把自己的想法加进去&#xff0…

Bigemap软件在农业上面的一些应用

在农业工作者平时的工作应用中 Bigemap可以帮助农业用户更好地管理土地、作物和水资源 &#xff1b;提高农业生产效率和质量 &#xff1b;以及 野外调查定位&#xff0c;地层分析论证&#xff0c;水文地质调查等&#xff0c; 大部分的农业用户在Bigemap软件上需要使用到以下 的…

ViTPose+:迈向通用身体姿态估计的视觉Transformer基础模型 | 京东探索研究院

身体姿态估计旨在识别出给定图像中人或者动物实例身体的关键点&#xff0c;除了典型的身体骨骼关键点&#xff0c;还可以包括手、脚、脸部等关键点&#xff0c;是计算机视觉领域的基本任务之一。目前&#xff0c;视觉transformer已经在识别、检测、分割等多个视觉任务上展现出来…

IOC和注解

想要学好spring&#xff0c;必须时时刻刻想着&#xff0c;spring的本质就是一个容器&#xff0c;放java对象的容器&#xff0c;java对象在spring容器中也叫做bean对象。 文章目录 一、spring介绍1、什么是框架2、框架的作用![在这里插入图片描述](https://img-blog.csdnimg.cn…

0014Java程序设计-springboot旅行景点推荐系统

摘要目 录概述1.1研究背景1.2 开发意义1.3 研究现状1.4 研究内容1.5 论文结构 系统实现开发环境 摘要 互联网的广泛运用给生活带来很多便捷。 因而&#xff0c;将旅游地介绍与现如今互联网紧密结合&#xff0c;利用Java技术搭建旅游地强烈推荐系统&#xff0c;完成旅游地强烈推…

港陆证券:电子竞技传来重磅消息!概念股上半年业绩普增

国际奥委会宣布建立电子竞技委员会。 据央视新闻报道&#xff0c;北京时间9月6日&#xff0c;国际奥委会在官网发布音讯&#xff0c;国际奥委会有史以来将初次展望电子竞技的未来&#xff0c;建立一个全新的电子竞技委员会。 国际奥委会主席巴赫表明&#xff0c;虚拟体育有着…

Geopy 笔记:计算距离

1 介绍 Geopy使用测地线距离或大圆距离来计算两点之间的地理距离&#xff0c;其中默认使用测地线距离&#xff08;geopy.distance.distance&#xff09; 大圆距离&#xff08;great_circle&#xff09;使用地球的球形模型&#xff0c;&#xff0c;半径为6371.0087714150598 公…

AJAX学习笔记9 搜索联想自动补全

AJAX学习笔记8 跨域问题及解决方案_biubiubiu0706的博客-CSDN博客 其实就一个功能 搜索联想 自动补全 键盘按下事件keydown 键盘弹起事件keyup 做模糊查询 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><t…

海外ASO优化之如何优化游戏应用

如果我们发布了一款手机游戏或者管理了一款手机游戏&#xff0c;那么需要确保我们的手机游戏对合适的人可见&#xff0c;目的是增加应用的下载量。 1、优化游戏元数据的关键词。 Apple和Google在应用商店中为我们提供有限的空间&#xff0c;来描述手机游戏及其优势。我们需要使…

事件派发触发以及自定义事件派发dispatchEvent-——————派发键盘事件

事件派发触发以及自定义事件派发dispatchEvent 首先DOM的方法 addEventListener() 和 removeEventListener()是用来分配和删除事件的函数。 这两个方法都需要三个参数&#xff0c;分别为&#xff1a;事件名称&#xff08;String&#xff09;、要触发的事件处理函数(Function)、…

Run in PaddleX!四步搞定10+任务场景36个精选产业模型开发与部署!

随着ChatGPT引领的AI破圈&#xff0c;各行各业掀起了AI落地的潮流&#xff0c;从智能客服、智能写作、智能监控&#xff0c;到智能医疗、智能家居、智能金融、智能农业&#xff0c;谁能快速将AI与传统业务相结合&#xff0c;谁就将成为企业数字化和智能化变革的优胜者。然而&am…

10、Kubernetes核心技术 - Label标签

目录 一、概述 二、Label Selector&#xff08;标签选择器&#xff09; 1)、基于等值&#xff08;Equality-based&#xff09; 2)、基于集合&#xff08;Set-based&#xff09; 三、Label相关操作 (1)、yaml文件定义标签 (2)、查看标签 (3)、筛选标签 (4)、添加标签 …

Unity VideoPlayer 指定位置开始播放

如果 source是 videoclip&#xff08;以下两种方式都可以&#xff09;&#xff1a; _videoPlayer.Play();Debug.Log("time: " _videoPlayer.clip.length);_videoPlayer.time 10; [SerializeField] VideoPlayer videoPlayer;public void SetClipWithTime(VideoClip…

iTOP-i.MX6ULL开发板修改 samba 配置文件

sudo vi /etc/samba/smb.conf 添加如下内容&#xff1a; 这些信息都是 samba 的说明和设置&#xff0c;把这些复制上&#xff0c;格式要设置对&#xff0c;使用 Tab 键缩进&#xff0c;然后把注释删 除&#xff0c;不然可能会出错。 [ubuntu_samba] comment arm ubuntu sa…

HashMap、LinkedHashMap和TreeMap:你真的了解它们吗?

亲爱的小伙伴们&#xff0c;大家好呀&#xff01;我是小米&#xff0c;一个热衷于技术分享的90后程序员。今天我要和大家聊聊一个在面试中经常会被问到的话题&#xff1a;HashMap、LinkedHashMap、TreeMap的区别。这可是一个非常重要的知识点&#xff0c;不仅在面试中会被频繁提…