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一層層去分析也會發現一些有意思的東西。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM