java审计(三)
单例模式下如何通过反射实现命令执行
getMehtod和invoke方法
getMethod
的作用是通过反射获取一个类的某个特定的公有方法,java中支持类的重载,我们不能仅通过函数名来确定一个函数,所以在调用getMethod的时候,我们需要传入需要获取的函数的参数类型列表
例子:
getMethod("exec",String.class) |
invoke
的作用是执行方法,它的第一个参数是:
1.如果是普通方法,那么第一个参数是类对象
2.如果是静态方法,则第一个参数是类
payload:
Class clazz= Class.forName("java.lang.runtime"); |
将上述paylaod分解完以后可得
Class clazz=Class.forName("java.lang.runtime"); |
getConstructor
1.getConstructor
接收的参数是构造函数列表类型,
2.获取到构造函数后,使用newInstance
来执行
eg:
使用反射获取ProcessBuilder的构造函数,然后调用start()来执行命令
Class clazz=Class.forName("java.lang.ProcessBuilder"); |
ProcessBuilder
有两个构造函数:
1.public ProcessBuilder(List<string> command)
2.public ProcessBuilder(String... command)
但是第一个形式使用了强制类型转化(区别看下面)
但是在表达式上下文中没有此语法,因此需要利用反射
Class clazz = Class.forName("java.lang.ProcessBuilder");clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))); |
通过getMethod(“start”)获取到start方法,然后invoke执行,invoke的第一个参数就是ProcessBuilder Object。
对比一下两者的区别,前者是强制转化为函数来执行命令了,后者是通过反射调用类和方法来执行。
可变长参数
可变长参数,java在编译的时候会编译成一个数组
public void hello(String[] names){} |
用法
对于反射而言,如果要获取的目标函数里包含可边长参数,则只需将其作为数组即可。
将字符串数组的类String[].class传给getConstructor,获取ProcessBuilder的第二种构造函数:
Class clazz=class.forName("java.lang.ProcessBuilder"); |
在调用newInstance
的时候,接受的是一个可变长参数,我们传给ProcessBuilder的也是,二者叠加变成二维数组
payload:
Class clazz = Class.forName("java.lang.ProcessBuilder");((ProcessBuilder)clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start(); |
私有方法进行反射
getDeclared
:
1.getMethod系列方法获取的是当前类中的所有公共方法,包括从父类继承的方法
2.getDeclaredMethod系列方法获取的是当前类中声明的方法,是写在这个类里的,包括私有的方法,但从父类哪里继承来的就不包含了
差异:大概就是getDeclaredMethod可以直接获得到类的私有对象并且可以实例化
Class clazz = Class.forName("java.lang.ProcessBuilder");((ProcessBuilder)clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start(); |
而setAccessible
作用在于修改作用域,否则无法调用
小结
看了这么多,大概总结一下,其实就是区分不同情况如何实现命令执行,单利模式,私有方法,使用getMethod或者使用getConstructor,或者是getDeclared来调用其方法,然后在这之前需要用forname,或者是其他方法来获取到这个对象,才可调用其方法