前言
之前Java学了个寂寞,很多东西都没有涉及到,做项目也只是写业务逻辑CRUD没涉及到很多Java特性,一边整理姿势一边了解下Java参考
本文参考了Epicccal师傅的文章
Java 反射
定义如下:
Java 反射机制是指在程序运行时 , 对于任何一个类 , 都能知道这个类的所有属性和方法 , 对于任何一个实例对象 , 都能调用该对象的任何一个属性和方法 .
Java中这种 “ 动态获取信息 “ 和 “ 动态调用属性方法 “ 的机制被称为 Java 反射机制.
实例对象可以通过反射机制获取它的类 , 类可以通过反射机制获取它的所有方法和属性 . 获取的属性可以设值 , 获取的方法可以调用 .
简单地来说就是补充了Java作为静态语言在调用方法和属性上不灵活的缺点,通过这种机制得以动态的创建对象和调用其方法属性(写到这里有个疑问"打断点的时候能访问对象属性是不是就是因为这个反射机制的存在")
Java反射的机制的功能:
- 在程序运行时查找一个对象所属类
- 在程序运行时查找任意一个类的成员变量和方法
- 在程序运行时构造任意一个类的对象
- 在程序运行时调用任意一个方法的对象
查找一个对象所属类
obj.getClass()
obj.forName(className)
className.class
查找一个类的方法
className.getMethod(functionName , [parameterTypes.class])
className.getMethods()
className.getDeclaredMethods()
构造任意一个类的对象
className.newInstance()
这个方法已被废弃,但不影响使用
使用这个函数的时候默认会调用无参数的构造函数
执行前提:
- 类必须要有无参构造函数 .
- 类的构造函数不能是私有的 , 也就是不能通过 “
private
“ 修饰符来修饰构造函数 .
className.getConstructor( parameterType ).newInstance( parameterName )
会返回所有构造方法的一个子集,即public修饰符修饰的
调用任意一个实例对象的方法
Method.invoke(obj , args[])
demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class reflectTest { public String print(String name) { return name; }
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InvocationTargetException {
String name = "harry"; Class<?> cls = Class.forName("reflectTest"); reflectTest obj = (reflectTest) cls.getDeclaredConstructor().newInstance(); Object ret = cls.getMethod("print", String.class).invoke(obj, name); System.out.println(ret);
} }
|
RCE
基本过程:
获取命令执行的类,使用Class.forName()
查看类中所有方法,使用className.getMethods()
找到可利用的方法构造invoke,Method.invoke(obj , args[])
obj是构造出来的类,method是想要执行的方法,args是需执行的命令
正常情况访问是 静态方法:类名.方法名
成员函数:对象名.方法名
invoke就相当于反过来,方法名.invoke(类名)
或者方法名.invoke(对象名)
可以利用的类一般有两种:
java.lang.RunTime
java.lang.ProcessBuilder
无参RCE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader;
public class reflect2RCE {
public reflect2RCE() { }
public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException { Class<?> cls = Class.forName("java.lang.Runtime"); System.out.println("通过Class.forName()方法获取任意类" + cls); Method[] methods = cls.getMethods(); for (Method m : methods) { System.out.println(m); } }
|
然后和刚刚一样试着invoke调用exec方法.
1 2 3
| Class<?> cls = Class.forName("java.lang.Runtime"); Object obj = cls.getMethod("exec", String.class).invoke(cls.newInstance(), "ls");
|
报错,因为这个java.lang.Runtime
中构造函数是私有的(ClassName.instance()这个函数会调自动用的无参构造函数)
绕过:
使用反射时,类初始化的时候会对类中所有的静态方法进行调用,下图是源码,所以只要完成一个runtime类的初始化即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader;
public class reflect2RCE {
public reflect2RCE() { }
public void reflect_runtime() throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException { Class<?> cls = Class.forName("java.lang.Runtime");
Method mGetRuntime = cls.getMethod("getRuntime"); Method mExec = cls.getMethod("exec", String.class); Object obj = mGetRuntime.invoke(null);
Process p = (Process) mExec.invoke(obj, "ls"); InputStream is = p.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } }
public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException { reflect2RCE r = new reflect2RCE(); r.reflect_runtime();
}
}
|
RCE成功
如何访问私有方法
之前的调用Runtime执行系统命令没有直接调用它的那个私有方法,而是通过 java.lang.Runtime
执行系统命令时 , 由于该类的构造方法 Runtime()
是一个私有方法 , 所以我们不能调用该方法 , 只能通过 getRuntime()
静态方法来返回一个 Runtime
实例对象 , 然后再调用 exec()
方法
className.getDeclaredConstructor()
会返回所有的构造方法,包括protected,private.
访问私有类可以将此函数的返回值(Constructor <?> )附加一个参数className.setAccessible(true);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public void reflect_runtime() throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException { Class<?> cls = Class.forName("java.lang.Runtime"); Method mExec = cls.getMethod("exec", String.class);
Constructor<?> cst = cls.getDeclaredConstructor(); cst.setAccessible(true); Object obj = cst.newInstance();
Process p = (Process) mExec.invoke(obj, "ls"); InputStream is = p.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } }
|
带参RCE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class reflect2RCE {
public reflect2RCE() { }
public void reflect_processBuilder() throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException { Class<?> cls = Class.forName("java.lang.ProcessBuilder"); Method startCmd = cls.getMethod("start"); Object obj = cls.getConstructor(List.class).newInstance(Arrays.asList("uname", "-a"));
Process p = (Process) startCmd.invoke(obj); InputStream is = p.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } }
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { reflect2RCE r = new reflect2RCE(); r.reflect_processBuilder();
}
}
|
总结
通过反射来执行RCE的基本流程就是
- 构造存在RCE的类
- 构造存在RCE类中具体方法method
- 构造此类的实例对象obj
- 用实例对象obj和具体方法method使用invoke执行RCE