JAVA审计之命令执行篇
0x00:前言
在Java中能执行命令的类其实并不多,不像php那样各种的命令执行函数。在Java中目前所知的能执行命令的类也就两种,分别是Runtime和 ProcessBuilder类。
0x01:Runtime 执行命令分析
这里是一个简单的示例:
Servlet("/execServlet")
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
public class execServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String exec = req.getParameter("exec");
Process res = Runtime.getRuntime().exec(exec);
InputStream inputStream = res.getInputStream();
ServletOutputStream outputStream = resp.getOutputStream();
int len;
byte[] bytes = new byte[1024];
while ((len = inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
} }
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
试一下执行命令
来看看他具体的实现,首先跟踪一下getRuntime()方法。
可见什么操作都没有直接返回了一个currentRuntime对象,也就是说每次调用getRuntime()方法都会new一个Runtime的对象。
官方文档说明:
每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行环境进行接口。 当前运行时可以从getRuntime方法获得。 应用程序无法创建自己的此类的实例。
再来跟踪一下exec方法,看看exec方法的具体实现
exec中有多个重载方法,继续跟踪返回值的exec
重载方法中又有重载方法,继续跟踪
这里会发现不一样了,没有继续重载,而是返回了 ProcessBuilder
在跟踪的时候会发现,我们传入的ipconfig会在cmdarray变量里面进行传入,其他值都是null。也就是说该类的底层其实还是ProcessBuilder这个类来进行实现的。
前面的
这里也可以根据idea提示得知
但我们后面的
abstract public InputStream getInputStream();
abstract public InputStream getErrorStream();
abstract public int waitFor() throws InterruptedException;
abstract public int exitValue();
abstract public void destroy();
跟踪一下,这几个方法,调用的方法,除了start方法外,其他的几个方法都是进行设置值。这里就不一一演示了。
跟踪start方法
可以看到start方法里面,调用了process的实现类。
0x02:ProcessBuilder中命令执行
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类
杀掉子进程。
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的构造函数是一个字符串列表或者数组。列表中第一个参数是可执行命令程序,其他的是命令行执行是需要的参数。