Java審計之命令執行篇
0x00 前言
在Java中能執行命令的類其實並不多,不像php那樣各種的命令執行函數。在Java中目前所知的能執行命令的類也就兩種,分別是Runtime和 ProcessBuilder類。
0x01 Runtime 執行命令分析
關於Runtime具體的使用可以看這篇文章,反射去調用Runtime。
@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一層層去分析也會發現一些有意思的東西。