Java 场景下的命令执行,不一定都表现为“传统 shell 注入”,很多时候是开发直接调用 Runtime、ProcessBuilder、脚本引擎或表达式引擎,导致用户输入能够触发系统命令、脚本执行或代码执行。
java.lang.Runtime
java.lang.ProcessBuilder
java.lang.ProcessImpl
javax.script.ScriptEngineManager
groovy.lang.GroovyShell
- 执行系统命令
- 文件读写
- 下载并执行恶意程序
- 反弹 Shell
- 结合表达式注入、反序列化、模板注入达成 RCE
Java 命令执行常见分成两类:
- 直接命令执行:开发显式调用系统命令
- 间接代码执行:用户输入进入脚本引擎、表达式引擎、Groovy 等动态执行组件
Runtime.getRuntime().exec(cmd)重点:
exec()默认不是把整串命令交给/bin/sh -c或cmd /c- 因此很多场景并非经典“分隔符命令注入”,而更偏向“参数注入”
- 如果开发手动套了 shell,例如
"/bin/sh", "-c", cmd",才更接近传统命令拼接注入
参考:
StringBuilder sb = new StringBuilder();
try {
String[] arrCmd = {cmd};
ProcessBuilder processBuilder = new ProcessBuilder(arrCmd);
Process p = processBuilder.start();
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String tmpStr;
while ((tmpStr = inBr.readLine()) != null) {
sb.append(tmpStr);
}
} catch (Exception e) {
return e.toString();
}
return sb.toString();重点关注:
- 参数数组是如何构造的
- 是否把用户输入直接作为完整命令
- 是否显式调用 shell
Class clazz = Class.forName("java.lang.ProcessImpl");
Method start = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
start.setAccessible(true);
start.invoke(null, new String[]{"calc"}, null, null, null, false);这类写法常见于反射调用或绕过某些表层审计规则的场景。
/*jsurl
var a = mainOutput();
function mainOutput() {
var x = java.lang.Runtime.getRuntime().exec("calc");
}
*/
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
String cmd = String.format("load(\"%s\")", jsurl);
engine.eval(cmd, bindings);重点:
- 动态脚本执行本身就极其危险
- 若支持远程加载脚本,风险更高
- 需关注 JDK 版本、脚本引擎实现和禁用情况
public void groovyshell(String content) {
GroovyShell groovyShell = new GroovyShell();
groovyShell.evaluate(content);
}Groovy 一旦接收用户可控表达式,通常很容易演变为高危代码执行。
- 后台执行诊断命令
- 插件系统、脚本系统、规则引擎
- 表达式求值功能
- 调试接口
- 任务调度、运维平台、CI/CD 平台
- 动态模板或 Groovy 扩展点
代码审计重点搜:
Runtime.getRuntime().execnew ProcessBuilderScriptEngineManagerGroovyShell- 反射调用命令执行类
重点确认:
- 是作为完整命令
- 还是作为参数
- 还是进入脚本/表达式字符串
这会直接影响利用方式:
- 若经过 shell,优先尝试经典命令注入思路
- 若未经过 shell,更多考虑参数注入和程序行为利用
区分:
- 直接回显标准输出
- 报错回显
- 无回显但可出网
Java 里很多所谓“命令注入”本质是向系统命令追加危险参数,而不是简单拼 ;。
Windows 和 Linux 在:
- shell 语法
- 路径
- 可执行程序
- 参数格式
上都不同,payload 不能混用。
即使找不到 Runtime.exec(),也要继续看:
- 脚本引擎
- Groovy
- 表达式引擎
- 反序列化链
优先使用 Java 原生 API 处理文件、网络、压缩等操作。
如必须调用外部程序,命令和参数都应做严格白名单,不允许用户传完整命令行。
不要把用户输入传给 ScriptEngine、GroovyShell 或类似接口。
运行 Java 服务的账户不应具备高权限和敏感目录写权限。
- 先搜
Runtime、ProcessBuilder、ScriptEngineManager、GroovyShell - 先分清命令执行、参数注入还是脚本执行
- 判断是否经过 shell,再选择利用思路
- 检查是否存在标准输出回显、错误回显或外带能力
- 继续联动表达式注入、模板注入和反序列化链