标签 反序列化漏洞 下的文章

漏洞介绍

用友NC系统UserSynchronizationServlet方法存在反序列化漏洞,攻击者可执行任意命令,获取敏感信息。

影响版本

用友NC6.5

漏洞分析

漏洞位于nc.bs.pub.im.UserSynchronizationServlet接口

漏洞代码:

很明显的反序列化漏洞, 接收POST请求时,读取了输入流,之后对输入流方法进行in.readObject()造成了反序列化漏洞

接下来构造请求URL,要调用UserQueryServiceServlet类,只要在请求路由加上/~包名称+UserSynchronizationServlet类的完整路径即可

查看系统的Web.xml,可以看见请求/service和/servlet前缀的都经过NCInvokerServlet方法处理

NCInvokerServlet接口的doAction方法如下:

主要功能是获得url路径后,如果是以/~开头,截取第一部分为moduleName,然后再截取第二部分为serviceName,再根据getServiceObject(moduleName, serviceName)实现任意Servlet的调用。

这里包名称是uapim,再加上方法的完整路径就能进行请求了

所以这里的路由为:

用友反序列化漏洞存在cc依赖,这里就直接使用ysoserial-all.jar生成cc6的利用链请求dnslog,保存到本地的c.bin文件中

漏洞复现

0x01 简介

​ 主要还是看killer那个 ctf,然后以前实战也没怎么认真去打(坑太多了)。这次正好学习一下。

0x02 fastjson 加载

com.alibaba.fastjson.parser.ParserConfig#checkAutoType(java.lang.String, java.lang.Class<?>, int)

image.png

主要就是检查@type 指定的类

image.png
然后在判断时候在在反序化的map、缓存的map中,然后判断是不是白名单。

image.png

要是获取到就判断这些。不是期望类直接就包type not match。基本高版本要是不指定期望类,这一步就g了

0x03 写class后fastjson 加载机制(docbase)

image.png

如果我们利用cmonsio写入文件后, 这里都会获取不到,不再缓存 不是白名单,且这个classloader为null

image.png

这个时候就会调用classloader去获取这个class的流

image.png
这里清楚可以看到是sun.misc.Launcher$AppClassLoader

image.png

image.png

他的classpath路径jre的lib,jre下的class(默认没有)和项目的lib目录。

我们要是写文件在docbase目录下, 使用这个classloader是加载不到的。

image.png

最后来到这里

若果他是白名单类、jsonType,期望类的话。就会调用TypeUtils.loadClass(typeName, this.defaultClassLoader, cacheClass),要是这个类是白名单或者jsonType就会进行缓存

com.alibaba.fastjson.util.TypeUtils#loadClass(java.lang.String, java.lang.ClassLoader, boolean)

image.png

来到这里,这个defaulrclassloder是null,所以这里都是加载不到我们写入到docbase的类。

image.png

最后会来到这里。使用当前线程的classloader来加载

image.png

可以看到是webappclassloader

image.png

image.png

这里可以清楚看到docbase的目录。也就是说写入到docbase下的类要用webappclassloader才能加载到。

image.png

根据cache标志位,是否加入缓存。这cache就是前面提到的

image.png

image.png

最后又再次判断。

这也是为什么我写入到docbase后,要使用

{
"@type":"java.lang.Exception",
"@type":"org.example.Exception"
}

这种形式来加载,expectClassFlag这样为true,然后使用webappclassloaer加载。

0x04 fastjson 1.x 全版本饶过

再回到上面

image.png

如果我们获取到class的流,然后调用ClassReader读入,在字节信息中获取到jsonType信息,jsonType就会改为true。也就是完全可以写一个后门类,类打上@JSONType就行。

image.png

这样就能符合它的判断,jsontype标志位也变为true

image.png

最后加入缓存。这样1.2.83也能触发。

但是在cmonsio写文件下这种情况下没什么意义, 写docbase 继承期望类就能正常加载,不继承在过不了判断,无法使用webappclassload加载,也就获取不到类,写到jre/lib需要替换懒加载的jar包,毫无意义。

0x05 1.2.83 fastjson利用

在1.2.83的情况下,类名结尾为Exception或Error会直接返回null。

这个时候只能在sun.misc.Launcher$AppClassLoade来加载,也就是在jre下找利用,就是最经典的写懒加载jar包替换。

一般以chaset.jar、nashorn.jar,dnsns.jar 为主。

需要结合目录穿越写文件写到jre/lib目录。

image.png

一般在源码写上然后编译,这样不影响正常功能。

为了方便复现。这里只打包一个类

image.png

改成83 手动替换jar

image.png

image.png

image.png

0x06 commonsio 优化

org.apache.commons.io.input.CharSequenceInputStream

在commons-io 2.0-2.1上是没有的, 以及在高低版本上字节信息不同。c/cs

image.png

image.png

所以这里我套娃了一下,用org.apache.commons.io.input.CharSequenceReader的是配,这样io在2.0-2.7上都能利用。

再就是在不同系统os上,类随机到构造方法不同,导致写不了二进制数据。

image.png

io低版本会在linux随到decoder这个构造,不给decoder赋值,在解码流就会包空异常,

image.png

能利用的就是utf8,写不了二机制,只能利用ascii jar写入。实战千万别用,要是没打下目录,lib替换了影响服务。

image.png

随到这个就正常对charset赋值可以二进制数据。其余都没什么好说的了。

0x07 加入chains

​ 不得不说,fastjson真是java安全绕不过的大山。为此我也加入到chains。支持1.2.68 ,1.2.75-1.2.80.

io 2.0-2.7写文件

image.png

在能写二进制的情况下直接选就行

不能写二进制的话,使用

image.png

进行上传你要写的文件。

image.png

然后根据情况选择payload。

rerference

https://su18.org/post/fastjson-1.2.68/

https://flowerwind.github.io/2025/02/28/%E5%88%86%E4%BA%AB%E4%B8%80%E6%AC%A1%E7%BB%84%E5%90%88%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E6%8B%BF%E4%B8%8B%E7%9B%AE%E6%A0%87/

PyTorch 最新版本反序列化漏洞分析 前言 AI入门学习Transformers架构的时候,最忘不掉的无过于主播手把手教你用PyTorch手搓一个Transformers了,哈哈哈哈哈哈 一、漏洞描述 根本原因在于调用 torch.load 时使用了 weights_only=False,这启用了通过 pickle 模块进行的不安全反序列化,从而导致任意代码执行。torch.load 底层使用了 Python 的 pickle。当 weights_only=False 时,它会反序列化并重构文件中存储的任意 Python 对象。对来自不受信任或被篡改的检查点数据进行反序列化,会执行攻击者控制的代码,从而导致远程代码执行 (RCE)。 当加载 .pt2 归档文件时,torch.export.load() 遵循以下路径:它调用 load_pt2() 来枚举归档内容 → 进入 _load_exported_programs() → 然后调用 _load_constants()。如果归档中的任何常量被标记为 use_pickle=True 且其文件名以 tensor 开头,它就会执行 torch.load(io.BytesIO(constant_bytes), weights_only=False),从而触发 Python Pickle 反序列化。因此,如果 .pt2 文件包含恶意的 Pickle 常量,运行 torch.export.load() 可能会导致任意代码执行。 torch.export.load() 不允许配置 weights_only=True 参数。它总是执行 torch.load(io.BytesIO(constant_bytes), weights_only=False) —— 这个值是硬编码的。因此,加载恶意模型的用户不可避免地会面临被入侵的风险。

二、环境搭建 这个没有什么好搭建的,因为就是一个组件,下载最新版本的包就好了

三、漏洞分析 完整调用链追踪

漏洞代码分析 torch.export.load() 文件: torch/export/__init__.py

分析: 入口函数接收文件路径,调用 load_pt2() 加载 PT2 存档文件。

load_pt2() - 加载 PT2 存档 文件: torch/export/pt2_archive/_package.py

分析: load_pt2() 打开 PT2 存档文件,调用 _load_exported_programs() 处理模型文件。

_load_exported_programs() - 加载导出的程序 文件: torch/export/pt2_archive/_package.py

分析: _load_exported_programs() 遍历模型文件,调用 _load_constants() 加载常量数据。

_load_constants() - 加载常量(漏洞触发点) 文件: torch/export/pt2_archive/_package.py 这一步很关键,因为是有很多限制条件的

总结一下能够触发的条件 1path_name.startswith("tensor") 2payload_meta.use_pickle == True 3 weights_only=False 硬编码: 该值无法通过 torch.export.load() API 覆盖

torch.load() - Pickle 反序列化 文件: torch/serialization.py

weights_only=False 时,torch.load() 使用标准 pickle 反序列化,会触发对象的 __reduce__() 方法,导致任意代码执行。

四、漏洞复现 根据漏洞原理,步骤就是生成恶意文件和触发了 恶意模型生成脚本 文件: model.py

注意构造需要满足刚刚上面提到的条件

触发脚本 现在就是我们需要去触发了 文件: poc.py

然后我们查看文件

五、漏洞修复 由于最新版本还是存在这个漏洞,可能是正常功能需要????? 测试版本: PyTorch 2.11.0.dev20260110 六、参考资料

资源
链接
PyTorch 官方文档
CWE-502: 不安全的反序列化
Python Pickle 安全
赏金报告

0x01 简介

​ 主要还是看killer那个 ctf,然后以前实战也没怎么认真去打(坑太多了)。这次正好学习一下。

0x02 fastjson 加载

com.alibaba.fastjson.parser.ParserConfig#checkAutoType(java.lang.String, java.lang.Class<?>, int)

image.png

主要就是检查@type 指定的类

image.png
然后在判断时候在在反序化的map、缓存的map中,然后判断是不是白名单。

image.png

要是获取到就判断这些。不是期望类直接就包type not match。基本高版本要是不指定期望类,这一步就g了

0x03 写class后fastjson 加载机制(docbase)

image.png

如果我们利用cmonsio写入文件后, 这里都会获取不到,不再缓存 不是白名单,且这个classloader为null

image.png

这个时候就会调用classloader去获取这个class的流

image.png
这里清楚可以看到是sun.misc.Launcher$AppClassLoader

image.png

image.png

他的classpath路径jre的lib,jre下的class(默认没有)和项目的lib目录。

我们要是写文件在docbase目录下, 使用这个classloader是加载不到的。

image.png

最后来到这里

若果他是白名单类、jsonType,期望类的话。就会调用TypeUtils.loadClass(typeName, this.defaultClassLoader, cacheClass),要是这个类是白名单或者jsonType就会进行缓存

com.alibaba.fastjson.util.TypeUtils#loadClass(java.lang.String, java.lang.ClassLoader, boolean)

image.png

来到这里,这个defaulrclassloder是null,所以这里都是加载不到我们写入到docbase的类。

image.png

最后会来到这里。使用当前线程的classloader来加载

image.png

可以看到是webappclassloader

image.png

image.png

这里可以清楚看到docbase的目录。也就是说写入到docbase下的类要用webappclassloader才能加载到。

image.png

根据cache标志位,是否加入缓存。这cache就是前面提到的

image.png

image.png

最后又再次判断。

这也是为什么我写入到docbase后,要使用

{
"@type":"java.lang.Exception",
"@type":"org.example.Exception"
}

这种形式来加载,expectClassFlag这样为true,然后使用webappclassloaer加载。

0x04 fastjson 1.x 全版本饶过

再回到上面

image.png

如果我们获取到class的流,然后调用ClassReader读入,在字节信息中获取到jsonType信息,jsonType就会改为true。也就是完全可以写一个后门类,类打上@JSONType就行。

image.png

这样就能符合它的判断,jsontype标志位也变为true

image.png

最后加入缓存。这样1.2.83也能触发。

但是在cmonsio写文件下这种情况下没什么意义, 写docbase 继承期望类就能正常加载,不继承在过不了判断,无法使用webappclassload加载,也就获取不到类,写到jre/lib需要替换懒加载的jar包,毫无意义。

0x05 1.2.83 fastjson利用

在1.2.83的情况下,类名结尾为Exception或Error会直接返回null。

这个时候只能在sun.misc.Launcher$AppClassLoade来加载,也就是在jre下找利用,就是最经典的写懒加载jar包替换。

一般以chaset.jar、nashorn.jar,dnsns.jar 为主。

需要结合目录穿越写文件写到jre/lib目录。

image.png

一般在源码写上然后编译,这样不影响正常功能。

为了方便复现。这里只打包一个类

image.png

改成83 手动替换jar

image.png

image.png

image.png

0x06 commonsio 优化

org.apache.commons.io.input.CharSequenceInputStream

在commons-io 2.0-2.1上是没有的, 以及在高低版本上字节信息不同。c/cs

image.png

image.png

所以这里我套娃了一下,用org.apache.commons.io.input.CharSequenceReader的是配,这样io在2.0-2.7上都能利用。

再就是在不同系统os上,类随机到构造方法不同,导致写不了二进制数据。

image.png

io低版本会在linux随到decoder这个构造,不给decoder赋值,在解码流就会包空异常,

image.png

能利用的就是utf8,写不了二机制,只能利用ascii jar写入。实战千万别用,要是没打下目录,lib替换了影响服务。

image.png

随到这个就正常对charset赋值可以二进制数据。其余都没什么好说的了。

0x07 加入chains

​ 不得不说,fastjson真是java安全绕不过的大山。为此我也加入到chains。支持1.2.68 ,1.2.75-1.2.80.

io 2.0-2.7写文件

image.png

在能写二进制的情况下直接选就行

不能写二进制的话,使用

image.png

进行上传你要写的文件。

image.png

然后根据情况选择payload。

rerference

https://su18.org/post/fastjson-1.2.68/

https://flowerwind.github.io/2025/02/28/%E5%88%86%E4%BA%AB%E4%B8%80%E6%AC%A1%E7%BB%84%E5%90%88%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E6%8B%BF%E4%B8%8B%E7%9B%AE%E6%A0%87/


Apache NiFi GetAsanaObject 处理器反序列化漏洞(CVE-2025-66524)

漏洞描述

file-20260112204532791.png



简单看就是在GetAsanaObject处理器中可以反序列化任意类,影响版本为Apache NiFi 1.20.0至2.6.0

利用条件:

GetAsanaObject 处理器依赖可配置的 Distribute Map Cache Client Service 来存储和检索状态信息。

该处理器在处理缓存中的状态数据时,使用了 未经过滤的 Java 对象序列化与反序列化

攻击者若能向缓存服务器中写入恶意构造的状态数据,便可触发反序列化漏洞

环境搭建

在官网下载 Apache NiFi 2.6.0,下载地址:https://archive.apache.org/dist/nifi/2.6.0/

file-20260112204811665.png



下载解压后看到有个 lib 目录,里面是 nifi 的主要依赖。不过这里面有些 jar 包是在 nar 中不方便我们待会调试,还需要先写个脚本把 jar 提取出来

全部提取到新的 lib 目录后新建一个 java 项目把 lib 添加为依赖,配置远程调试信息

file-20260112205232708.png



然后在 nifi-2.6.0\conf\bootstrap.conf 中添加远程调试信息

file-20260112205342299.png



最后在 bin 目录下执行 nifi.cmd start 即可启动程序

漏洞分析&复现

运行后访问 https://localhost:8443/nifi/ 进入页面我们新建一个 GetAsanaObject 处理器,

file-20260112205700915.png



然后配置其属性,Asana Client Service|只有一个选项直接选就可以了,然后Distributed Cache Service 根据漏洞描述需要选择 map 那个,

file-20260112205723091.png



继续配置这两个 service,首先看 StandardAsanaClientProviderService 2.6.0,需要我们访问 asana 平台注册个账号配置 workspace 和 token,值得注意的是这个 workspace 要填 name 而不是 gid

file-20260112205952307.png



接着就是MapCacheClientService 2.6.0 了,只需要添加个 hostname 就可以了

file-20260112210202219.png



最后还要配置个 MapCacheServer,需要和 MapCacheClientService 相对应,这个 sevice 我们只用填 port 和 Persistence Directory 这两个属性就可以了,这样待会的缓存数据就会保存到 E:\tmp\test\test2 目录下面。

file-20260112210353755.png



启动 GetAsanaObject 处理器,这个处理器会不停的从 Asana 平台拉取指定类型的对象数据,并转换成 NiFi 的 FlowFile 供后续处理,而在这拉取数据写到缓存服务器和从缓存服务器读取数据的时候这里就发生了序列化和反序列化。

file-20260112210452875.png



根据 https://github.com/apache/nifi/pull/10599/commits/12351ba71bb32b5d725060e0c0236478ccdf4b05 ,定位到 GetAsanaObject 类,在其 recoverState 方法下上断点,会先调用到 client.get 方法读取缓存服务器数据,

file-20260112211050019.png



跟进来到 MapCacheClientService 的 get 方法

file-20260112211140116.png



赋值inboundAdapterValueInboundAdapter 对象,valueDeserializerValueInboundAdapter 对象的 deserializer 属性,

file-20260112211333866.png



继续跟进这个 get 方法,调用了valueAdapter.getResult() 方法

file-20260112211435002.png



最后会一直调用到 getValue 方法,看到其实这个 value 是上面 deserializer.deserialize(bytes); 获得的

file-20260112211947956.png



而这个 deserializer 其实是上面的 STATE_MAP_VALUE_SERIALIZER 值也就是 GenericObjectSerDe 对象,所以最后会来到 GenericObjectSerDe.deserialize 方法,在这里进行了反序列化,

file-20260112212138766.png



反序列化数据为缓存服务器中的 value 也就是对应的 hashmap 部分,

file-20260112212209545.png


file-20260112213223986.png



以上就是反序列化流程了,当然为了控制输入流我们还需要看看写的流程,定位 MapCacheClientService#put 方法,看到就是直接对 key 和 value 进行序列化然后写入

file-20260112211600961.png


file-20260112211647772.png



所以我们自己写到缓存服务器的数据也要同时对 key 和 value 都进行序列化才能让后面 GetAsanaObject 处理器成功读取到我们自己写入的数据实现任意反序列化。

让 ai 写个脚本通过 java 代码来进行写入,注意缓存服务器地址要对应,

这里的 key 可以在 statushistory 里面看到,

file-20260112213535782.png



运行后成功写入数据

file-20260112213637554.png



file-20260112213648634.png



然后重新启动 GetAsanaObject 处理器,看到成功反序列化到了我们写入的数据,实现了可以反序列化任意类的利用

file-20260112213746146.png


file-20260112213807808.png



漏洞修复

file-20260112215755380.png



GenericObjectSerDe 换为了 StringSerDeMapStringSerDe 对象,

file-20260112215603629.png



然后 deserialize 方法改为了直接返回字符串,

file-20260112215944740.png



参考

https://github.com/apache/nifi/pull/10599/commits/12351ba71bb32b5d725060e0c0236478ccdf4b05

https://avd.aliyun.com/detail?id=AVD-2025-66524


PyTorch Distributed Checkpoint 远程代码执行漏洞分析

漏洞描述





PyTorch 的分布式检查点模块中的 FileSystemReader 类在 read_metadata() 方法中使用不安全的 pickle.load() 函数反序列化检查点元数据文件,且未对数据来源进行任何验证。攻击者可以通过构造包含恶意 pickle 数据的检查点文件,当受害者使用 FileSystemReader 加载该检查点时,恶意代码将在反序列化过程中自动执行,从而实现任意代码执行。

环境搭建

直接最新的版本就可以

漏洞复现

PoC 代码









为什么需要 MaliciousMetadata 对象呢?


因为 read_metadata() 期望反序列化后得到一个 Metadata 对象

如果直接 pickle.dump(MaliciousPayload()),反序列化时会报错

所以构造一个Metadata 的对象,把恶意负载嵌进去



漏洞分析

漏洞原理并不难,就是根据漏洞点去构造触发文件

FileSystemReader,漏洞点

文件位置: torch/distributed/checkpoint/filesystem.py

这个类也是会读取 read_metadata,metadata,并反序列化处理





很明显,在这个点上

无输入验证: 未验证 .metadata 文件来源
无类白名单: 允许反序列化任意 Python 类
自动调用: 加载检查点时自动触发
完全信任用户输入: path 参数完全由用户控制





Pickle 反序列化原理

Python 的 pickle 模块用于对象的序列化和反序列化。在反序列化过程中,pickle 会自动调用对象的 __reduce__() 方法来重建对象

我们在 __reduce__() 写入恶意代码

攻击场景

可以分为一些场景

场景一: HuggingFace 模型投毒

场景二: 训练脚本 CLI 注入

场景三: MLOps 平台上传

总结

通过数据流向,让 AI 画一个图,很清晰

漏洞修复

官方并没有修复,因为被认定为重复漏洞,但是最新版本还是可以复现,下面是一些修复方法

Unpickler/白名单

修复后的 read_metadata() 方法:

添加 safe_mode 参数



参考资料

CWE-502: Deserialization of Untrusted Data

Python pickle Documentation

OWASP Deserialization Cheat Sheet

赏金报告