Java反射-RCE

Java反射-RCE

Java安全的开始要从反射开始.反射是指在程序运行时 , 对于任何一个类 , 都能知道这个类的所有属性和方法 , 对于任何一个实例对象 , 都能调用该对象的任何一个属性和方法 .

能拿到这个类,你就可以获取和调用了解关于这个类的任何信息.

类 对象 Class对象

class对象就是由Class类创建的对象,只是这个创建过程不是由用户主动创建的,而是由JVM自动实现的。JVM会为项目中每一个类(不论是JDK自带的类、第三方jar包的类、还是自己编写的类)创建一个class对象,用来保存与这个类相关的信息。class对象只与类有关,与对象无关。我们从一张图来看它们之间的关系。

img

获得对象类

  • obj.getClass()
  • obj.forName(className)
  • className.class

查找类方法

  • className.getMethod(functionName , [parameterTypes.class])
  • className.getMethods()
  • className.getDeclaredMethods()
  • className.getConstructor()

新建类示例

  • className.newInstance()

调用类方法

  • Method.invoke(obj , args[])

访问私有化

  • obj.setAccessible(true)

Test.java

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
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;

public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
reflectRunCalc();
}

public static void reflectRunCalc() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
var cls = Class.forName("java.lang.Runtime");//获取到Runtime对象
var run = cls.getDeclaredMethod("getRuntime");//获取Runtime
Object obj = run.invoke(null);
Method mExec = cls.getMethod("exec", String.class);//获取
Process p = (Process) mExec.invoke(obj, "ping 127.0.0.1");
InputStream is = p.getInputStream();

// 设置字符集为中文(GBK 或 UTF-8,根据系统和命令输出情况选择合适的编码)
InputStreamReader isr = new InputStreamReader(is, Charset.forName("GBK"));
BufferedReader br = new BufferedReader(isr);

String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}

上面的代码首先通过Class.forName获取java.lang.Runtime类,之后通过getRuntime方法获取Runtime示例,之后调用exec方法来进行命令执行,后续通过InputStreamReader来捕获输出,完成了一次反射调用,在没有显式导入java.lang.Runtime情况下完成了一次命令执行.

读取进程输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.*;

import static java.lang.System.out;

public class ProcessExecutor {
public static void main(String[] args) throws Exception {
Process process = Runtime.getRuntime().exec("whoami");
var inputStream = process.getInputStream();
var reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
out.println(line);
}
}
}