关于Hessian2二次反序列化中我学到了几点
字数 2184 2025-08-29 08:31:54
Hessian2二次反序列化与Rome链利用技术详解
前言
本文深入分析Hessian2通过Rome链进行两次反序列化完成恶意EvilTemplatesImpl注入的技术细节。重点探讨TemplatesImpl的触发机制、Hessian2反序列化特性以及SignedObject在绕过限制中的作用。
1. TemplatesImpl触发机制详解
TemplatesImpl是Java XML处理中的一个关键类,其触发机制涉及以下关键方法:
1.1 方法调用链
-
getOutputProperties()
- 这是触发链的入口点
- 会调用newTransformer()方法
-
newTransformer()
- 调用getTransletInstance()来创建恶意类
- 是实际触发恶意代码执行的关键环节
-
getTransletInstance()
- 会进入defineTransletClasses()方法
- 需要满足两个条件才能进入defineTransletClasses:
_name不能为空_class必须为空
1.2 defineTransletClasses关键点
在defineTransletClasses方法中,有几个需要特别注意的点:
-
_tfactory属性
- 这是需要特别关注的关键属性
- 它是为何需要调用二次反序列化的关键原因
- 在第一次反序列化时可能为空,导致NullPointerException
-
类定义过程
- 负责加载和定义恶意类
- 需要正确的字节码和类名设置
2. Hessian2反序列化特性
2.1 序列化限制
Hessian2对序列化有以下重要限制:
-
transient字段处理
- Hessian2Input与Hessian2Output均不能对transient修饰的成员进行序列化或反序列化
- 这是与Java原生序列化的重要区别
-
ObjectInput/ObjectOutput行为
- 除非相关类重写了readObject或writeObject方法
- 否则也无法操作transient修饰的成员变量
2.2 TemplatesImpl的特殊处理
TemplatesImpl类有以下特殊行为:
-
_tfactory属性
- 虽然是transient修饰
- 但重写了readObject方法,会在其中生成_tfactory的实例
- 这是解决NullPointerException的关键
-
二次反序列化的必要性
- 第一次反序列化时_tfactory可能为空
- 通过重写的readObject方法可以正确初始化_tfactory
- 使得后续的getOutputProperties调用不会抛出异常
3. SignedObject的利用
3.1 SignedObject特性
SignedObject在sofastack/sofa-hessian的黑名单中被发现,具有以下特点:
-
序列化机制
- 使用ObjectInput.readObject和ObjectOutput.writeObject操作Serializable类
- 字节码存储在this.content属性中
-
反序列化触发
- 可以通过getObject函数触发反序列化
- 因为是getter方法,可以在ToStringBean的toString方法中被调用
3.2 解决_tfactory为空的问题
SignedObject可以用于解决TemplatesImpl中_tfactory为空的问题:
-
封装机制
- 将TemplatesImpl封装在SignedObject中
- 通过getObject触发完整的反序列化过程
-
示例代码
public static void main(String[] args) throws Exception {
// 创建恶意TemplatesImpl对象
TemplatesImpl templates = createMaliciousTemplates();
// 创建SignedObject封装
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(templates, privateKey, signature);
// 触发反序列化
TemplatesImpl evil = (TemplatesImpl) signedObject.getObject();
}
3.3 完整利用链
-
第一次反序列化
- Hessian2反序列化SignedObject
- 此时TemplatesImpl的_tfactory可能未正确初始化
-
第二次反序列化
- 通过getObject触发完整反序列化
- TemplatesImpl的readObject被调用,正确初始化_tfactory
- 后续调用getOutputProperties不会抛出异常
4. 完整攻击流程
-
构造恶意TemplatesImpl
- 设置_name不为空
- 确保_class为空
- 包含恶意字节码
-
封装到SignedObject
- 利用SignedObject封装恶意对象
- 准备触发二次反序列化
-
Hessian2序列化
- 将SignedObject序列化为Hessian2格式
- 准备发送给目标
-
目标反序列化
- 目标使用Hessian2反序列化
- 触发SignedObject的getObject
- 完成TemplatesImpl的完整反序列化
- 通过Rome链触发恶意代码执行
5. 防御建议
-
黑名单机制
- 将SignedObject加入反序列化黑名单
- 限制危险类的反序列化
-
输入验证
- 严格验证反序列化的数据来源
- 避免不可信数据的反序列化
-
安全配置
- 使用最新版本的序列化库
- 应用安全补丁
6. 参考资源
- CommonsCollections3分析 - 笑花大王
- Java"后反序列化漏洞"利用思路 - Ruilin
- Gadget1.java - Ruilin
- Hessian 反序列化漏洞分析 - D4ck
通过深入理解这些技术细节,安全研究人员可以更好地防御这类攻击,同时也能够更有效地进行安全测试和漏洞挖掘。