某平台Groovy任意代码执行思考

原理小分析

exeGroovyScript方法传入了两个参数,其中scriptParameters是base64编码的json格式

image

在exeGroovyScript方法中,又调用了exeGroovyScript方法

image

exeGroovyScript方法里会将传入的参数做解析

image

parse方法:

image

具体解析操作发生在CompilationUnit类,由于太难懂了就不继续往下看CompilationUnit类了。大概意思就是CompilationUnit类会根据传入的对象解析生成AST(抽象语法树,对象的各个属性用节点来表示),然后根据AST生成对应的字节码。回到exeGroovyScript方法

image

此时的shell就是生成的字节码文件Script[int].class,它是Script的子类,生成的文件会重写run方法

image

根据代码流程,Script[int].class的入口方法即run方法,生成的class大概如下

.groovy脚本

image

编译的class

image

言归正传,最后InvokerHelper.createScript会创建Script[int].class的实例,具体看到createScript方法

image

最终newScript会根据Binding提供的上下文信息,获取一个有参构造函数创建实例

image

纵观源码,发现其最后就是反射动态调用

 

利用手法

根据整理,Groovy的命令执行大概有3种利用

1
2
3
4
5
6
7
8
9
10
1、可双可单参数写法
groovy.lang.GroovyShell parse 先解析语句
org.codehaus.groovy.runtime.InvokerHelper createScript 再运行

2、单参数写法
groovy.lang.GroovyShell evaluate 直接执行

3、单参数写法2
groovy.lang.GroovyShell parse 先解析
groovy.lang.Script run() 再运行

首先说下参数语法

image

execute()用到的是Runtime.getRuntime().exec()

image

text是从Process对象的输入流中读取文本数据并返回

image

第一种用法

也是前面案例的用法,Payload不变,向Binding传递一个Map对象

image

可以看到如果payload是"whoami".execute().text,那么hashMap可为空,执行不受影响,双引号内的字符串将以参数的形式传递到execute()进行执行

 

另外因为传递的是hashMap对象,所以这个payload也可以使用占位符的方式进行利用

image

原因是在代码Constructor constructor = scriptClass.getConstructor(Binding.class);中,Binding.class指定了Constructor的参数类型,即Binding类型。

constructor.newInstance(context)中,会使用context对象作为参数创建实例。如果没有找到相应的构造方法,就会执行script = (Script)scriptClass.newInstance();,然后再通过script.setBinding(context)设置context,也就是说,map里的value会被当做scriptClass的参数执行

image

从编译的class中可以看到,是有Binding类型的有参构造函数的

image

URL中可以这样表示:

1
A=x.execute().text&B={“x”:”whoami”}

第二种用法

image

第三种用法

image

结尾

所以在审计时可以关注三种用法场景,三种用法都可以执行java代码或者groovy语法




声明:
本文章用于学习交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意本声明。

Disclaimer:
This article is for study and communication. It is strictly forbidden to use it for illegal operations. All consequences shall be borne by yourself. Reading this article means that you have agreed to this statement.