java-Groovy
简述
Groovy是Apache 旗下的一种基于JVM的面向对象编程语言,既可以用于面向对象编程,也可以用作纯粹的脚本语言。在语言的设计上它吸纳了Python、Ruby 和 Smalltalk 语言的优秀特性,比如动态类型转换、闭包和元编程支持。 Groovy与 Java可以很好的互相调用并结合编程 ,比如在写 Groovy 的时候忘记了语法可以直接按Java的语法继续写,也可以在 Java 中调用 Groovy 脚本。比起Java,Groovy语法更加的灵活和简洁,可以用更少的代码来实现Java实现的同样功能。
漏洞版本
Groovy : 1.7.0-2.4.3
漏洞分析
MethodClosure
1 | package org.example; |
先从这个类开始入手 (就是最后执行代码的地方)
org.codehaus.groovy.runtime.MethodClosure
是方法闭包,使用闭包代表了一个对象的一个方法,可以很方便的调用。
MethodClosure 初始化时接收两个参数,一个是对象,一个是对象的方法名称。
MethodClosure 中有一个 doCall 方法,调用 InvokerHelper.invokeMethod()
方法进行方法调用。
这里就是先使用MethodClosure
来进行传值 分别是传一个对象和这个对象的某个方法
由于这个doCall
()是个protected
方法 得使用反射调用
这个最后一步的意思就是反射调用这个doCall()
方法,然后传值是这个calc
m:就是等会会调用doCall
这个方法
invoke(mc,"calc")
这个里面的mc
指的是调用这个MethodClosure
里的doCall
方法 calc
是这个daCall
的参数
String.execute() 方法
Groovy 为 String 类型添加了 execute()
方法,以便执行 shell 命令,这个方法会返回一个 Process 对象。也就是说,在 Groovy 中,可以直接使用 "ls".execute()
这种方法来执行系统命令 “ls”。
1 | package org.example |
这里切记生成文件的要选groovy,不然会执行不了
其实就是使用Runtime.getRuntime().exec()
来执行
如果在java中可以这样写
1 | package org.example; |
分析一波 下个断点
调用栈
1 | // 直接命令执行 |
ConvertedClosure
org.codehaus.groovy.runtime.ConvertedClosure
是一个通用适配器,用于将闭包适配到 Java 接口。ConvertedClosure 实现了 ConversionHandler 类,而 ConversionHandler 又实现了 InvocationHandler。所以说 ConvertedClosure 本身就是一个动态代理类。
ConvertedClosure 的构造方法接收一个 Closure 对象和一个 String 类型的 method 方法名,也就是说 ConvertedClosure 会代理这个 Closure 对象,当调用其 method 方法时,将会调用 ConvertedClosure 父类的 invoke
方法,除了 toString 和一些默认方法外,会调用 invokeCustom
方法。
如果初始化时指定的 method 与 invokeCustom
指定的 method 参数相同,则 invokeCustom
方法将会调用代理对象 Closure 的 call 方法执行传入参数执行。
看到这里就明白这条链的触发逻辑了。后面自然是使用 AnnotationInvocationHandler
将 ConvertedClosure
代理成 Map
类。这样在反序列化
攻击构造
最终的POC
1 | public class Groovy { |
调用链展示
1 | AnnotationInvocationHandler.readObject() |
调用entrySet,然后触发invoke
调用了ConversionHandler#invoke()
方法
接着就会调用到ConvertedClosure#invokeCustom()
方法 并且由于这个
methodName
和这个传进来的method
的name
一样 就会调用call
()方法
这里的话到最后就rce了