java审计(三)

单例模式下如何通过反射实现命令执行

getMehtod和invoke方法

getMethod的作用是通过反射获取一个类的某个特定的公有方法,java中支持类的重载,我们不能仅通过函数名来确定一个函数,所以在调用getMethod的时候,我们需要传入需要获取的函数的参数类型列表

例子:

getMethod("exec",String.class)

invoke的作用是执行方法,它的第一个参数是:
1.如果是普通方法,那么第一个参数是类对象
2.如果是静态方法,则第一个参数是类

payload:

Class clazz= Class.forName("java.lang.runtime");
clazz.getMethod("exec",String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz),"calc.exec");

将上述paylaod分解完以后可得

Class clazz=Class.forName("java.lang.runtime");
Method execMethod=clazz.getMethod("exec",String.class);
Method getRuntimeMethod=clazz.getMethod("getRuntime");
Object runtime =getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime,"calc.exec");

getConstructor

1.getConstructor接收的参数是构造函数列表类型,
2.获取到构造函数后,使用newInstance来执行

eg:
使用反射获取ProcessBuilder的构造函数,然后调用start()来执行命令

Class clazz=Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.getConstructor(List.class).newInstance(Arrays.asList("cacl.exe"))).start();

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){}
public void hello(String...names){}

用法

对于反射而言,如果要获取的目标函数里包含可边长参数,则只需将其作为数组即可。

将字符串数组的类String[].class传给getConstructor,获取ProcessBuilder的第二种构造函数:

Class clazz=class.forName("java.lang.ProcessBuilder");
clazz.getConstructor(String[].class)

在调用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,或者是其他方法来获取到这个对象,才可调用其方法

Author

vague huang

Posted on

2022-03-17

Updated on

2022-03-30

Licensed under

Comments