某平台Groovy任意代码执行思考
原理小分析
exeGroovyScript方法传入了两个参数,其中scriptParameters是base64编码的json格式
在exeGroovyScript方法中,又调用了exeGroovyScript方法
exeGroovyScript方法里会将传入的参数做解析
parse方法:
具体解析操作发生在CompilationUnit类,由于太难懂了就不继续往下看CompilationUnit类了。大概意思就是CompilationUnit类会根据传入的对象解析生成AST(抽象语法树,对象的各个属性用节点来表示),然后根据AST生成对应的字节码。回到exeGroovyScript方法
此时的shell就是生成的字节码文件Script[int].class
,它是Script的子类,生成的文件会重写run方法
根据代码流程,Script[int].class
的入口方法即run方法,生成的class大概如下
.groovy脚本
编译的class
言归正传,最后InvokerHelper.createScript
会创建Script[int].class
的实例,具体看到createScript方法
最终newScript会根据Binding提供的上下文信息,获取一个有参构造函数创建实例
纵观源码,发现其最后就是反射动态调用
利用手法
根据整理,Groovy的命令执行大概有3种利用
1 |
|
首先说下参数语法
execute()用到的是Runtime.getRuntime().exec()
,
text是从Process对象的输入流中读取文本数据并返回
第一种用法
也是前面案例的用法,Payload不变,向Binding传递一个Map对象
可以看到如果payload是"whoami".execute().text
,那么hashMap可为空,执行不受影响,双引号内的字符串将以参数的形式传递到execute()进行执行
另外因为传递的是hashMap对象,所以这个payload也可以使用占位符的方式进行利用
原因是在代码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的参数执行
从编译的class中可以看到,是有Binding类型的有参构造函数的
URL中可以这样表示:
1 |
|
第二种用法
第三种用法
结尾
所以在审计时可以关注三种用法场景,三种用法都可以执行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.