从 CVE-2026-42779 说起:为什么你写的 resolveClass 防不住反序列化漏洞
原创 KCyber 2026-05-06 07:01 北京

欢迎大家关注自在安全公众号。为更好学习交流,建了个技术交流群,大家可以扫描进群。你也可以关注公众号后@我拉你进群。

引言
自 2015年Java原生反序列化漏洞被公开以来,十多年间各类防御方案层出不穷。无论是JDK层面的JEP290机制,还是通过重写ObjectInputStream的resolveClass实现黑白名单过滤,在理论上都能有效阻断恶意gadget。但是防御的有效性高度依赖于代码的具体实现。除了挖掘新的gadget绕过黑白名单限制外,过滤逻辑本身的缺陷也会成为被利用的突破口。本文从近期披露的CVE-2026-42779说起,结合近年来多个真实CVE 漏洞案例,剖析反序列化防御在代码层面常见的几个典型误区。
CVE-2026-42779:误用 forClass 导致的逻辑绕过
近期 Apache MINA 披露的 CVE-2026-42779 就是一个典型案例。在此前的 CVE-2024-52046 反序列化漏洞修复中,Apache MINA 在触发点 AbstractIoBuffer.getObject 的 resolveClass 环节引入了白名单检测:

初看之下该修复方案似乎无懈可击,直接丢给 AI 来看也没找出什么问题。但深入分析发现,与常见直接通过 desc.getName 提取类名进行检查有所不同,它首先会使用 desc.forClass 来提取 clazz ,并仅在 clazz 非空时才会执行白名单检查。
查看 ObjectStreamClass.forClass 源码可知,返回值是否为 null 完全取决于私有字段 cl 的赋值状态:

追踪 ObjectStreamClass 的构造链路发现, cl 可以通过私有构造函数赋值:

该私有构造函数在 lookup 中被调用,cl 直接被赋值为传入的 Class 对象:

回到 getObject的调用栈,当type=1时readClassDescriptor会调用lookup,使得forClass返回非null,符合白名单检查的预期。但当type=0时,cl没有被赋值并始终保持null ,导致白名单检测逻辑被完全跳过。通过构造特定的序列化流即可轻松实现绕过:
try (ObjectOutputStream oos = new ObjectOutputStream(baos) {
@Override protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {
write(0); // [1] 强制type=0
super.writeClassDescriptor(desc);
}
}) {
oos.writeObject(obj);
oos.flush();
}
...
CVE-2026-42779 的最终修复方案如下,核心思路就是将白名单检查前置,确保无论 forClass 结果如何都会执行校验:

该漏洞产生的根本原因就是检查逻辑依赖一个不可靠的前提:forClass 非空,导致攻击者可以在数据流层面构造特定参数实现绕过。
Top Class Level Error:仅检测顶层类的逻辑陷阱
Java 反序列化中 Top Class Level 的错误防御模式也比较常见,以下是我近年来调试过的几个真实案例:
• CVE-2021-3287 Zoho ManageEngine OpManager SUM Bypass
• CVE-2023-34040 Spring Kafka ErrorHandlingDeserializer Bypass
• 某友 FileManageServlet Bypass
• ...
以 CVE-2021-3287 为例, Zoho ManageEngine OpManager 反序列化触发点 ITOMObjectInputStream 的实现如下:其 resolveClass 仅在 classResolved 为 false (对应解析最外层类)时才执行白名单检查,后续嵌套对象的解析直接放行:

无独有偶,CVE-2023-34040 中 Spring Kafka 的 ErrorHandlingDeserializer 也存在类似逻辑:

针对这种 Top Class Level Error 的绕过思路就是:在最外层包装一个白名单允许的类(比如 DeserializationException ) ,将实际的 gadget 藏在该类的属性中(这与 .NET 中类 BinaryFormatter 的反序列化利用方式有些类似), 这样构造的 gadget 即可绕过 resolveClass 中的检测 :
package org.springframework.kafka.support.serializer;
import java.io.Serializable;
import java.util.Set;
public class DeserializationException implements Serializable{
private static final long serialVersionUID = 8280022391259546509L;
private Object gadget;
public DeserializationException(Object set) {
this.gadget = gadget;
}
}resolveClass 检测断链:二次反序列化与自定义类加载
resolveClass 的检测机制是在类描述符解析阶段触发的,这种逐层套娃式的检查链条在某些情况下会被中断。
JRMP/UnicastRef 绕过
一个经典例子就是 JRMP 反序列化中的 UnicastRef 绕过思路:
static {
proxyBlackList.add("java.rmi.registry");
classBlackList.add("sun.rmi.server.UnicastRef");
}
@Override // java.io.ObjectInputStream
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
Iterator<String> it = classBlackList.iterator();
while (it.hasNext()) {
String s = it.next();
if (desc.getName().contains(s)) {
throw new ClassNotFoundException(s);
}
}
return super.resolveClass(desc);
}UnicastRef 重写了readExternal方法,而java.rmi.server.RemoteObject的readObject会调用该方法。由于resolveClass在检查到RemoteObject不在黑名单后,将调用其readObject。此时内部还没有来得及对UnicastRef进行检查就会执行readExternal ,检测链条被切断,从而实现了绕过。

Weblogic 历史上的多次绕过
在 Weblogic T3/IIOP 反序列化漏洞绕过史上,类似的场景屡见不鲜,比如:
•
CVE-2016-0638:MarshalledObject.resolveClass函数将加载字节数组objBytes然后调用readObject进行二次反序列化,这种机制会跳过外层过滤。•
CVE-2020-14841:RemoteConstructor.readResolve内部存在defineClass和createInstance过程,通过自定义类加载实现RCE的过程完全不受resolveClass过滤机制的影响。• ...
CVE-2023-31099 合法的二次反序列化
再比如 CVE-2023-31099 中,Zoho ManageEngine Opmanager 自定义的 ITOMObjectInputStream 采用了白名单检测机制,仅允许反序列化 DataObject 这个合法类 :

问题在于, readObject获取dataObject对象后,会继续调用transferDataToNOC,经过一系列判断后进入getObjectData,该函数内部会对来自DataObject类的字节数组属性data进行一次全新的readObject,这次反序列化使用默认的ObjectInputStream,完全不受ITOMObjectInputStream.resolveClass 白名单的限制,最终导致防御失效。

防御方式的思考
除了 resolveClass,还可以使用Apache Commons IO的ValidatingObjectInputStream这种第三方库,或者JDK原生的JEP290过滤Filter来构建防御。然而这些只是可供选择的手段,有效性最终仍取决于过滤逻辑是否严密。反序列化防御的核心不在于是否使用了某个安全机制,是否正确理解了反序列化的完整处理过程才是关键。从CVE-2026-42779对forClass的误用,到Top Class 检验的片面性,再到二次反序列化导致的检测断链,这些漏洞证明任何逻辑上的微小错误,都可能导致反序列化防御机制的失效。
由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,公众号及文章作者不为此承担任何责任。
欢迎大家关注自在安全公众号。为更好学习交流,建了个技术交流群,大家可以扫描进群。你也可以关注公众号后@我拉你进群。
