JDBC反序列化初步学习
当在JDBC连接数据库的URL可控,且服务器存在可以利用的反序列化链,这时就可能存在反序列化漏洞
复现环境
JDK8 + Mysql 8.0
JDBC Maven
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
JDBC连接数据库代码
package org.vul;
import java.sql.DriverManager;
public class JDBC {
public static void main(String[] args) throws Exception{
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3309/jdbc?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true&serverTimezone=UTC";
String username = "root";
String password = "root";
DriverManager.getConnection(url,username,password);
}
}
漏洞分析
连接数据库URL中关键的地方就三个
- url中的目标地址是可控的,那么连接到哪个mysql服务就可控,可以编写一个恶意的mysql服务,这个后面会提到
- queryInterceptors属性相当于一个拦截器,连接代码中指定为
com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor
类,当执行数据库查询操作时,就经过ServerStatusDiffInterceptor类的postProcess和preProcess方法,在连接数据库时也会调用到preProcess方法 - autoDeserialize属性是利用反序列化需要用到的,这个后面会提,剩下的useSSL这些属性都是为了防止连接数据库时报错加上去的!
漏洞产生点就在ServerStatusDiffInterceptor#preProcess
方法上
跟进一下populateMapWithSessionStatusValues方法,这里执行了一个SHOW SESSION STATUS
查询操作,然后调用resultSetToMap方法,并把查询结果作为方法的参数。前面不是提到url中的目标地址可控吗,那么就可以编写一个恶意mysql服务器,让服务器在客户端查询SHOW SESSION STATUS
时返回的内容可控
跟进resultSetToMap方法,调用rs.getObject
方法,rs就是查询SHOW SESSION STATUS
返回的结果,那么我们编写恶意mysql服务器让其返回ResultSetImpl对象,就可以调用到ResultSetImpl#getObject
方法,为什么选择这个对象?因为这个对象有反序列化操作!
跟进ResultSetImpl#getObject
方法,一系列的操作最终会执行到反序列化操作,而反序列化的内容data变量可以通过编写的恶意mysql服务器控制!然后就是看前面的一系列判断条件了,通过columnIndexMinusOne获取field,columnIndexMinusOne又是通过columnIndex计算出来的,调试的时候columnIndex为2,columnIndexMinusOne为1,也就是上一步的第二次调用getObject方法才进入反序列化。然后判断field的类型,当field类型为BIT或者BLOB类型时(case BLOB
里面的代码跟case BIT
是一样的),通过col