• Java审计之命令执行篇


    Java审计之命令执行篇

    0x00 前言

    在Java中能执行命令的类其实并不多,不像php那样各种的命令执行函数。在Java中目前所知的能执行命令的类也就两种,分别是Runtime和 ProcessBuilder类。

    0x01 Runtime 执行命令分析

    关于Runtime具体的使用可以看这篇文章,反射去调用Runtime。

    Java学习之反射篇

    @WebServlet("/execServlet")
    public class execServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String exec = request.getParameter("exec");
            Process res = Runtime.getRuntime().exec(exec);
    
            InputStream inputStream = res.getInputStream();
            ServletOutputStream outputStream = response.getOutputStream();
    
            int len;
            byte[] bytes = new byte[1024];
            while ((len = inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
    
    
            }
    

    这里来运行一下,传入一个命令看看能不能正常运行。

    http://localhost:8080/untitled9_war_exploded/execServlet?exec=ipconfig
    

    我们来看看他具体的实现,首先跟踪一下getRuntime()方法。

    看到调用方法就不做任何的处理就直接返回了currentRuntime对象,上面可以看到currentRuntime是Runtime的实例对象。也就是说每次调用getRuntime()方法都会new一个Runtime的对象。

    官方文档说明:

    每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。 当前运行时可以从getRuntime方法获得。 
    应用程序无法创建自己的此类的实例。
    

    再来跟踪一下exec方法,看看exec方法的具体实现

    exec中有多个重载方法,在跟踪返回值的exec

    还是他的重载方法,再跟踪

    这里会发现不一样了,返回了

    return new ProcessBuilder(cmdarray)
                .environment(envp)
                .directory(dir)
                .start();
    

    在跟踪的时候会发现,我们传入的ipconfig会在cmdarray变量里面进行传入,其他值都是null。也就是说该类的底层其实还是ProcessBuilder这个类来进行实现的。

    前面的Process res = Runtime.getRuntime().exec(exec);返回类型为 Process,是通过new ProcessBuilder并传入命令,再去调用start方法返回得来的。

    但我们后面的res.getInputStream();,getInputStream();是怎么来的呢?Process 是一个抽象类,包含了6个抽象方法。

    abstract public OutputStream getOutputStream();
    
    
    abstract public InputStream getInputStream();
    
    
    abstract public InputStream getErrorStream();
    
    
    abstract public int waitFor() throws InterruptedException;
    
    
    abstract public int exitValue();
    
    
    abstract public void destroy();
    

    跟踪一下,这几个方法,调用的方法,除了start方法外,其他的几个方法都是进行设置值。这里就不一一演示了。

    return new ProcessBuilder(cmdarray)
                .environment(envp)
                .directory(dir)
                .start();
    

    跟踪start方法

    可以看到start方法里面,调用了process的实现类。

    0x02 ProcessBuilder中命令执行

    package com.test;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    
    @WebServlet("/exec2Servlet")
    public class exec2Servlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String exec = request.getParameter("exec");
            ServletOutputStream outputStream = response.getOutputStream();
            ProcessBuilder processBuilder = new ProcessBuilder(exec);
    
            Process res = processBuilder.start();
            InputStream inputStream = res.getInputStream();
            int len ;
            byte[] bytes =new byte[1024];
            while ((len = inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
            }
    
        }
    }
    
    

    使用ProcessBuilder的方式也是可以执行命令的。

    0x03 内容补充

    后面专门开一个标题,做一个前面知识的补充。

    Process类

    Process类方法:

    destroy()
    
    杀掉子进程。
    
    exitValue()
    
    返回子进程的出口值。
    
    InputStream getErrorStream()
    
    获得子进程的错误流。
    
    InputStream getInputStream()
    
    获得子进程的输入流。
    
    OutputStream getOutputStream()
    
    获得子进程的输出流。
    
    waitFor()
    
    导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止。
    

    前面会发现的一个问题在这里做一个解疑,process既然是抽象类,不能new对象,获取到他的实例类的呢?

    前面我们调试代码的时候,发现了可以使用ProcessBuilder的Start方法进行返回,第二种方法就是Runtime的exec方法,但Runtime的exec方法底层依然是ProcessBuilder的Start方法进行实现的。

    这里还有必要说的一个问题,ProcessBuilder与Runtime.exec()的区别是什么?

    在网上查阅了一些思路得出了以下的结论。

    ProcessBuilder.start() 和 Runtime.exec() 方法都被用来创建一个操作系统进程(执行命令行操作),并返回 Process 子类的一个实例,该实例可用来控制进程状态并获得相关信息。
    
    ProcessBuilder.start() 和 Runtime.exec()传递的参数有所不同,Runtime.exec()可接受一个单独的字符串,这个字符串是通过空格来分隔可执行命令程序和参数的;也可以接受字符串数组参数。而ProcessBuilder的构造函数是一个字符串列表或者数组。列表中第一个参数是可执行命令程序,其他的是命令行执行是需要的参数。
    

    参考文章

    https://honeypps.com/java/process-builder-quick-start/
    

    0x04 结尾

    在遇到一些问题的时候,多打debug也是个不错的选择,多打debug能比较清晰的分析到问题所在。例如调试代码的时候,可以打个debug一层层去分析也会发现一些有意思的东西。

    WX:TG9yaTI1NDgyNjYxNDU= 欢迎各位师傅来一起做技术交流
  • 相关阅读:
    产品小细节中的大体验
    产品经理的四点思考:不该简单满足用户需求
    产品经理的十大顶级错误
    SQL Server数据库大型应用解决方案总结
    java中public static void main(String[] args)中String[] args代表什么意思?
    异常处理1
    java中的String
    华为2013年西安java机试题目:如何过滤掉数组中的非法字符。
    2用java代码实现冒泡排序算法(转载)
    1用java实现冒泡排序算法以及解决的几个小问题。
  • 原文地址:https://www.cnblogs.com/nice0e3/p/13708704.html
Copyright © 2020-2023  润新知