java安全-危险类

一、Runtime命令执行

1.Runtime类

  • RuntimeJDK 自带、位于 java.lang 包中的类,每个 JVM 进程内部只有一个 Runtime 实例。

正常调用计算机方式:

1
2
Runtime rt = Runtime.getRuntime();
rt.exec("calc.exe");

2.类详解

类中方法

  • exec方法

    1
    2
    3
    4
    5
    6
    7
    8
    public Process exec(String[] cmdarray, String[] envp, File dir)
    throws IOException {
    return new ProcessBuilder(cmdarray)
    .environment(envp)
    .directory(dir)
    .start();
    }
    rt.exec("calc.exe");
  • getRuntime方法

    • 因为他是单例类,所以在创建类时需要使用其内部提供的构造方法

      1
      2
      3
      4
      5
      6
      Method getRuntimeMethod = clazz.getMethod("getRuntime");
      Runtime runtime = (Runtime) getRuntimeMethod.invoke(null);
      Method execMethod = clazz.getMethod("exec", String.class);
      execMethod.invoke(runtime, "calc.exe");

      Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"calc");//最短代码
  • 实现命令回显

    当你调用 exec 时,它返回的是一个 java.lang.Process 对象。命令的执行结果(比如 whoami 输出的用户名)是在这个 Process 对象的 InputStream(输入流) 里的。于是,实现下面步骤即可

    1. 执行 exec,得到 Process 对象。
    2. 反射调用ProcessgetInputStream() 方法。
    3. 读取流中的数据。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Class<?> clazz = Class.forName("java.lang.Runtime");
    Method getRuntimeMethod = clazz.getMethod("getRuntime");
    Runtime runtime = (Runtime) getRuntimeMethod.invoke(null);
    Method execMethod = clazz.getMethod("exec", String.class);

    // 执行命令
    Object process = execMethod.invoke(runtime, "ipconfig");

    Class<?> processClass = Class.forName("java.lang.Process");

    Method getInputStreamMethod = processClass.getMethod("getInputStream");
    java.io.InputStream is = (java.io.InputStream) getInputStreamMethod.invoke(process);
    Scanner scanner = new Scanner(is, "GBK").useDelimiter("\\A");
    String output = scanner.hasNext() ? scanner.next() : "";

    System.out.println("命令回显结果:\n" + output);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       public static void main(String[] args) throws Exception {  
    InputStream inputStream = Runtime.getRuntime().exec("whoami").getInputStream();
    byte[] cache = new byte[1024];
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    int readLen = 0;
    while ((readLen = inputStream.read(cache))!=-1){
    byteArrayOutputStream.write(cache, 0, readLen);
    }
    System.out.println(byteArrayOutputStream);
    }

二、ProcessBuilder命令执行

1.ProcessBuilder类

  • ProcessBuilder 是 Java 推荐的用于创建操作系统进程的方式。

正常调用计算机方式:

1
2
ProcessBuilder processBuilder = new ProcessBuilder("calc");
processBuilder.start();

2.类详解

  • 构造方法

    1
    2
    public ProcessBuilder(List<String> command) {
    public ProcessBuilder(String... command) {

    传入的参数即为执行命令,但是需要分开传递,例如:ProcessBuilder pb = new ProcessBuilder("ping", "-n", "3", "www.google.com");

  • start方法

    这是实际执行命令的方法。它会返回一个 Process 对象,代表正在运行的子进程。

  • 反射链调用

    1
    2
    3
    4
    5
    6
    7
    传参为列表
    Class clazz = Class.forName("java.lang.ProcessBuilder");
    clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc")));
    //这里传参为列表,
    传参为数组
    Class clazz = Class.forName("java.lang.ProcessBuilder");
    clazz.getMethod("start").invoke(clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc"}}));//这里传参为数组
  • 回显命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       public static void main(String[] args) throws Exception {  
    InputStream inputStream = new ProcessBuilder("ipconfig").start().getInputStream();
    byte[] cache = new byte[1024];
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    int readLen = 0;
    while ((readLen = inputStream.read(cache))!=-1){
    byteArrayOutputStream.write(cache, 0, readLen);
    }
    System.out.println(byteArrayOutputStream);
    }

三、ProcessImpl命令执行

1.ProcessImpl类

  • 平时用的Runtime.exec()或者 ProcessBuilder.start(),其实都只是“中介”。它们最终都会调用 java.lang.ProcessImpl(在 Windows 上)或 java.lang.UNIXProcess(在 Linux/JDK8 及更早版本上)来真正执行系统命令。

他没有公开构造方法,所以只能通过反射调用该类

2.类详解

  • start方法

    1
    2
    3
    4
    5
    static Process start(String cmdarray[],
    java.util.Map<String,String> environment,
    String dir,
    ProcessBuilder.Redirect[] redirects,
    boolean redirectErrorStream)

    具体参数如上,他是一个静态方法

    依旧返回Process对象

3.反射链

  • 回显

    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
        public static void main(String[] args) throws Exception {
    // 1. 获取底层类
    Class<?> clazz = Class.forName("java.lang.ProcessImpl");

    // 2. 获取 start 静态方法

    Method startMethod = clazz.getDeclaredMethod("start",
    String[].class, // 1. cmdarray
    Map.class, // 2. environment
    String.class, // 3. dir
    ProcessBuilder.Redirect[].class, // 4. redirects (这里是数组类型!)
    boolean.class // 5. redirectErrorStream
    );

    // 3. 暴力破解权限
    startMethod.setAccessible(true);

    // 4. 构造参数
    String[] cmd = new String[]{"ipconfig"};

    // 5. 调用 invoke
    // 注意第4个参数传 null。
    // 根据源码:if (redirects == null) { stdHandles = ... }
    // 传 null 会让它自动去生成默认的 stdHandles,非常完美。
    Object processObj = startMethod.invoke(null,
    cmd,
    null, // environment
    null, // dir
    null, // redirects (关键点:传 null)
    false // redirectErrorStream
    );

    // --- 6. 命令回显部分 ---
    // 拿到 Process 对象,读取输出流
    java.lang.Process process = (java.lang.Process) processObj;

    java.io.InputStream is = process.getInputStream();
    // Windows 默认编码通常是 GBK
    Scanner scanner = new Scanner(is, "GBK").useDelimiter("\\A");
    String output = scanner.hasNext() ? scanner.next() : "";

    System.out.println("--- ProcessImpl 成功调用 ---");
    System.out.println(output);
    }
    }