Java審計之命令執行篇

Java審計之命令執行篇

0x00 前言

在Java中能執行命令的類其實並很少,不像php那樣各類的命令執行函數。在Java中目前所知的能執行命令的類也就兩種,分別是Runtime和 ProcessBuilder類。php

0x01 Runtime 執行命令分析

關於Runtime具體的使用能夠看這篇文章,反射去調用Runtime。html

Java學習之反射篇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的對象。學習

官方文檔說明:ui

每一個Java應用程序都有一個Runtime類的Runtime ,容許應用程序與運行應用程序的環境進行接口。 當前運行時能夠從getRuntime方法得到。 
應用程序沒法建立本身的此類的實例。

再來跟蹤一下exec方法,看看exec方法的具體實現this

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

相關文章
相關標籤/搜索