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);
关键流程:
- 通过
SerializerFactory.getDefaultSerializer()获取序列化器 - 自定义类默认使用
UnsafeSerializer - 非 Serializable 类可通过
setAllowNonSerializable(true)支持 - transient 和 static 字段不参与序列化
1.3 反序列化过程分析
// 反序列化示例
Object o = new HessianInput(bais).readObject();
关键特性:
- 使用
Unsafe.allocateInstance()直接实例化对象 - 不执行构造方法、getter、setter 方法
- 对于 Map 类型,执行
MapDeserializer.readMap()方法 - 触发点:
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 通用利用条件
- 起始点限制:必须从 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
本文档基于技术社区公开资料整理,仅供安全研究学习使用。