Hessian 反序列化链汇总
字数 2087 2025-12-06 12:07:15

Hessian 反序列化链深入分析与利用指南

一、Hessian 反序列化基础原理

1.1 Hessian 序列化特点

Hessian 采用自定义的序列化协议,不遵循 Java 原生序列化规则:

  • 起始方法限制:只能从 hashCode/equals/compareTo 方法开始
  • 字段限制:利用链中调用的成员变量不能为 transient 修饰
  • 逻辑限制:不依赖类中 readObject 逻辑,也不依赖 getter/setter 逻辑

1.2 序列化过程分析

// 序列化示例代码
SerializerFactory serializerFactory = new SerializerFactory();
serializerFactory.setAllowNonSerializable(true); // 允许非Serializable类
HessianOutput heout = new HessianOutput(baos);
heout.setSerializerFactory(serializerFactory);
heout.writeObject(test);

关键流程:

  1. 通过 SerializerFactory.getDefaultSerializer() 获取序列化器
  2. 自定义类默认使用 UnsafeSerializer
  3. 非 Serializable 类可通过 setAllowNonSerializable(true) 支持
  4. transient 和 static 字段不参与序列化

1.3 反序列化过程分析

// 反序列化示例
Object o = new HessianInput(bais).readObject();

关键特性:

  1. 使用 Unsafe.allocateInstance() 直接实例化对象
  2. 不执行构造方法、getter、setter 方法
  3. 对于 Map 类型,执行 MapDeserializer.readMap() 方法
  4. 触发点:map.put()equals/hashCode/compareTo 方法调用

二、主要利用链分析

2.1 Remo 链(JNDI注入)

依赖要求:

  • Rome 库
  • JdbcRowSetImpl 类

调用栈:

HashMap.put() → EqualsBean.hashCode() → ToStringBean.toString() 
→ JdbcRowSetImpl.getDatabaseMetaData() → JNDI lookup

关键代码构造:

Vector<String> strMatchColumns = new Vector<>();
strMatchColumns.add("username");
setFieldValue(jdbcRowSet, "strMatchColumns", strMatchColumns);
// 防止 ToStringBean 执行 getter 时因空字段抛出异常

限制说明:

  • 无法直接使用 TemplatesImpl(_tfactory 为 transient)
  • 依赖 JNDI 注入,需要出网或本地搭建恶意 JNDI 服务

2.2 TemplatesImpl + SignedObject 二次反序列化

技术原理:
利用 SignedObject 的 getObject() 方法执行原生 Java 反序列化:

// SignedObject.getObject() 内部逻辑
public Object getObject() throws IOException, ClassNotFoundException {
    return new ObjectInputStream(new ByteArrayInputStream(this.content)).readObject();
}

优势:

  • 绕过 Hessian 对 transient 字段的限制
  • 可正常使用 TemplatesImpl 实现字节码加载
  • 支持完整的原生 Java 反序列化链

2.3 JDK 原生链(无依赖)

核心思路:
通过 HashMap 的哈希冲突触发 Hashtable.equals() 方法调用

初次构造(错误示例):

HashMap hashMap = new HashMap();
Hashtable hashtable = new Hashtable();
hashMap.put(hashtable, "test"); // 不会触发equals

正确构造(哈希冲突):

// 创建两个 UIDefaults 对象(Hashtable子类)
UIDefaults uiDefaults1 = new UIDefaults();
UIDefaults uiDefaults2 = new UIDefaults();

// 精心构造使其发生哈希冲突
hashMap.put(uiDefaults1, "test1");
hashMap.put(uiDefaults2, "test2"); // 触发 uiDefaults1.equals(uiDefaults2)

完整 EXP 构造:

// 利用 Hashtable.equals() 中的代码执行点
if (!value.equals(t.get(key))) {
    return false;
}
// 通过精心构造实现任意代码执行

2.4 Resin 链

依赖要求:

  • Resin 相关类
  • 需要设置 trustCodeBase=true

调用栈:

XString.equals() → QName.toString() → ... → NamingManager.getObjectInstance()

哈希冲突构造:
通过逆向计算 QName 和 XString 的 hashCode,精心构造使其发生冲突:

// 特殊的哈希冲突构造算法
QName qName = new QName("", "");
XString xString = new XString(""); // 精心构造的字符串

2.5 XBean 链

技术原理:
与 Resin 链类似,通过 ContextUtil$ReadOnlyBinding 的 toString() 方法触发:

调用栈:

HotSwappableTargetSource.equals() → ContextUtil$ReadOnlyBinding.getObject() 
→ ContextUtil.resolve() → NamingManager.getObjectInstance()

利用特点:

  • HotSwappableTargetSource 提供稳定的 hashCode 返回值
  • 易于构造哈希冲突条件

2.6 Spring 相关链

2.6.1 PartiallyComparableAdvisorHolder 链

技术要点:

  • 使用 Unsafe 实例化避开构造方法限制
  • 防止构造方法中的异常抛出
// Unsafe 方式实例化
Unsafe unsafe = getUnsafe();
Object instance = unsafe.allocateInstance(targetClass);

2.6.2 AbstractBeanFactoryPointcutAdvisor 链

特点:

  • 调用链简单直接
  • 依赖 Spring 相关类

三、技术要点总结

3.1 通用利用条件

  1. 起始点限制:必须从 hashCode/equals/compareTo 开始
  2. 字段限制:避免使用 transient 字段
  3. 逻辑限制:不依赖构造方法、readObject、getter/setter

3.2 哈希冲突利用技巧

  1. 双重put触发:通过两次 put 操作制造哈希冲突
  2. 子类利用:使用 Hashtable 子类(如 UIDefaults)避免自比较死循环
  3. 逆向计算:对特定类的 hashCode 算法进行逆向工程

3.3 绕过限制的方法

  1. 二次反序列化:通过 SignedObject 跳转到原生 Java 反序列化
  2. Unsafe 实例化:避开构造方法限制
  3. Map 特性利用:利用 HashMap/TreeMap 的特定行为

四、防御建议

4.1 代码层面

  1. 对 transient 字段进行合理使用
  2. 避免在 hashCode/equals/compareTo 中执行敏感操作
  3. 对反序列化操作进行白名单控制

4.2 配置层面

  1. 设置 SerializerFactory.setAllowNonSerializable(false)
  2. 使用最新版本的 Hessian 库
  3. 对反序列化源进行可信验证

五、参考资源

  1. Hessian 官方文档和安全公告
  2. Java 安全研究社区相关分析文章
  3. 漏洞利用工具:https://github.com/LINGX5/HessianExploit-tool

本文档基于技术社区公开资料整理,仅供安全研究学习使用。

Hessian 反序列化链深入分析与利用指南 一、Hessian 反序列化基础原理 1.1 Hessian 序列化特点 Hessian 采用自定义的序列化协议,不遵循 Java 原生序列化规则: 起始方法限制:只能从 hashCode/equals/compareTo 方法开始 字段限制:利用链中调用的成员变量不能为 transient 修饰 逻辑限制:不依赖类中 readObject 逻辑,也不依赖 getter/setter 逻辑 1.2 序列化过程分析 关键流程: 通过 SerializerFactory.getDefaultSerializer() 获取序列化器 自定义类默认使用 UnsafeSerializer 非 Serializable 类可通过 setAllowNonSerializable(true) 支持 transient 和 static 字段不参与序列化 1.3 反序列化过程分析 关键特性: 使用 Unsafe.allocateInstance() 直接实例化对象 不执行构造方法、getter、setter 方法 对于 Map 类型,执行 MapDeserializer.readMap() 方法 触发点: map.put() → equals/hashCode/compareTo 方法调用 二、主要利用链分析 2.1 Remo 链(JNDI注入) 依赖要求: Rome 库 JdbcRowSetImpl 类 调用栈: 关键代码构造: 限制说明: 无法直接使用 TemplatesImpl(_ tfactory 为 transient) 依赖 JNDI 注入,需要出网或本地搭建恶意 JNDI 服务 2.2 TemplatesImpl + SignedObject 二次反序列化 技术原理: 利用 SignedObject 的 getObject() 方法执行原生 Java 反序列化: 优势: 绕过 Hessian 对 transient 字段的限制 可正常使用 TemplatesImpl 实现字节码加载 支持完整的原生 Java 反序列化链 2.3 JDK 原生链(无依赖) 核心思路: 通过 HashMap 的哈希冲突触发 Hashtable.equals() 方法调用 初次构造(错误示例): 正确构造(哈希冲突): 完整 EXP 构造: 2.4 Resin 链 依赖要求: Resin 相关类 需要设置 trustCodeBase=true 调用栈: 哈希冲突构造: 通过逆向计算 QName 和 XString 的 hashCode,精心构造使其发生冲突: 2.5 XBean 链 技术原理: 与 Resin 链类似,通过 ContextUtil$ReadOnlyBinding 的 toString() 方法触发: 调用栈: 利用特点: HotSwappableTargetSource 提供稳定的 hashCode 返回值 易于构造哈希冲突条件 2.6 Spring 相关链 2.6.1 PartiallyComparableAdvisorHolder 链 技术要点: 使用 Unsafe 实例化避开构造方法限制 防止构造方法中的异常抛出 2.6.2 AbstractBeanFactoryPointcutAdvisor 链 特点: 调用链简单直接 依赖 Spring 相关类 三、技术要点总结 3.1 通用利用条件 起始点限制 :必须从 hashCode/equals/compareTo 开始 字段限制 :避免使用 transient 字段 逻辑限制 :不依赖构造方法、readObject、getter/setter 3.2 哈希冲突利用技巧 双重put触发 :通过两次 put 操作制造哈希冲突 子类利用 :使用 Hashtable 子类(如 UIDefaults)避免自比较死循环 逆向计算 :对特定类的 hashCode 算法进行逆向工程 3.3 绕过限制的方法 二次反序列化 :通过 SignedObject 跳转到原生 Java 反序列化 Unsafe 实例化 :避开构造方法限制 Map 特性利用 :利用 HashMap/TreeMap 的特定行为 四、防御建议 4.1 代码层面 对 transient 字段进行合理使用 避免在 hashCode/equals/compareTo 中执行敏感操作 对反序列化操作进行白名单控制 4.2 配置层面 设置 SerializerFactory.setAllowNonSerializable(false) 使用最新版本的 Hessian 库 对反序列化源进行可信验证 五、参考资源 Hessian 官方文档和安全公告 Java 安全研究社区相关分析文章 漏洞利用工具:https://github.com/LINGX5/HessianExploit-tool 本文档基于技术社区公开资料整理,仅供安全研究学习使用。