Deserialization Vulnerability in h2oai/h2o-3
Description
A deserialization vulnerability exists in h2oai/h2o-3 versions <= 3.46.0.8, allowing attackers to read arbitrary system files and execute arbitrary code. The vulnerability arises from improper handling of JDBC connection parameters, which can be exploited by bypassing regular expression checks and using double URL encoding. This issue impacts all users of the affected versions.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A deserialization vulnerability in h2o-3 <= 3.46.0.8 allows arbitrary file read and code execution via improper JDBC URL validation.
Vulnerability
Overview
CVE-2025-6544 is a deserialization vulnerability in the h2oai/h2o-3 machine learning platform, affecting versions up to and including 3.46.0.8. The root cause is improper handling of JDBC connection parameters. Specifically, the validation of JDBC URLs relies on regular expression checks that can be bypassed using double URL encoding [1][2]. This allows an attacker to inject arbitrary connection parameters.
Exploitation
Vector
An attacker can exploit this vulnerability by providing a malicious JDBC URL that is double URL-encoded. For example, encoding the URL such that characters like '%3A' (colon) and '%2F' (slash) are themselves encoded, evading the regex validation. The validation function in the affected versions does not recursively decode the URL before checking against the disallowed parameter list. The commit that addresses this issue [3] shows the addition of a decoding loop that repeatedly decodes the URL until no further changes occur, preventing such bypasses.
Impact
Successful exploitation enables an attacker to read arbitrary system files and execute arbitrary code on the server running the vulnerable H2O instance. This can lead to full compromise of the machine learning platform and any data it processes. The CVSS v4.0 score is not yet provided by NVD [2], but the vulnerability is severe due to remote code execution capabilities.
Mitigation
The vulnerability has been patched in subsequent commits. Users are strongly advised to upgrade to a version later than 3.46.0.8. The fix involves proper decoding of the JDBC URL before validation, as demonstrated in the referenced commit [3]. As of the publication date, no workarounds have been officially documented; upgrading to the latest release is the recommended course of action [1][2].
- GitHub - h2oai/h2o-3: H2O is an Open Source, Distributed, Fast & Scalable Machine Learning Platform: Deep Learning, Gradient Boosting (GBM) & XGBoost, Random Forest, Generalized Linear Modeling (GLM with Elastic Net), K-Means, PCA, Generalized Additive Models (GAM), RuleFit, Support Vector Machine (SVM), Stacked Ensembles, Automatic Machine Learning (AutoML), etc.
- NVD - CVE-2025-6544
- Follow up on GH-16622 - handle also string with URL encoding characte… · h2oai/h2o-3@0298ee3
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
ai.h2o:h2o-coreMaven | <= 3.46.0.7 | — |
h2oPyPI | <= 3.46.0.7 | — |
Affected products
2- h2oai/h2oai/h2o-3v5Range: unspecified
Patches
10298ee348f5cFollow up on GH-16622 - handle also string with URL encoding characters (#16631)
2 files changed · +41 −2
h2o-core/src/main/java/water/jdbc/SQLManager.java+21 −2 modified@@ -4,6 +4,7 @@ import water.fvec.*; import water.parser.ParseDataset; import water.util.Log; +import water.util.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -614,11 +615,29 @@ public static void validateJdbcUrl(String jdbcUrl) throws IllegalArgumentExcepti throw new IllegalArgumentException("JDBC URL is null or empty"); } - if (!jdbcUrl.toLowerCase().startsWith("jdbc:")) { + String previous = null; + String jdbcUrlDecode = jdbcUrl; + try { + for (int i = 0; i < 10; i++) { + previous = jdbcUrlDecode; + jdbcUrlDecode = URLDecoder.decode(jdbcUrlDecode, "UTF-8"); + if (previous.equals(jdbcUrlDecode)) { + break; + } + } + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("JDBC URL has wrong encoding"); + } + + if (!previous.equals(jdbcUrlDecode)) { + throw new IllegalArgumentException("JDBC URL contains invalid characters"); + } + + if (!jdbcUrlDecode.toLowerCase().startsWith("jdbc:")) { throw new IllegalArgumentException("JDBC URL must start with 'jdbc:'"); } - Matcher matcher = JDBC_PARAMETERS_REGEX_PATTERN.matcher(jdbcUrl); + Matcher matcher = JDBC_PARAMETERS_REGEX_PATTERN.matcher(jdbcUrlDecode); String property = System.getProperty(DISALLOWED_JDBC_PARAMETERS_PARAM); List<String> disallowedParameters = property == null ? DEFAULT_JDBC_DISALLOWED_PARAMETERS :
h2o-core/src/test/java/water/jdbc/SQLManagerTest.java+20 −0 modified@@ -226,6 +226,26 @@ public void testValidateJdbcConnectionStringMysqlOneParameter() { SQLManager.validateJdbcUrl(jdbcConnection); } + @Test + public void testValidateJdbcConnectionStringMysqlDoubleEncodedString() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("Potentially dangerous JDBC parameter found: allowLoadLocalInfile"); + + String jdbcConnection = "jdbc%3Amysql%3A%2F%2F127.0.0.1%3A3308%2Ftest%3F+%2561%256c%256c%256f%2577%254c%256f%2561%2564%254c%256f%2563%2561%256c%2549%256e%2566%2569%256c%2565%3Dtrue%26+%2561%256c%256c%256f%2577%2555%2572%256c%2549%256e%254c%256f%2563%2561%256c%2549%256e%2566%2569%256c%2565%3Dtrue&table=a&username=fileread_/etc/passwd&password=123123&fetch_mode=SINGLE"; + + SQLManager.validateJdbcUrl(jdbcConnection); + } + + @Test + public void testValidateJdbcConnectionStringMysqlMultipleEncodedString() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("JDBC URL contains invalid characters"); + + String jdbcConnection = "jdbc%2525252525252525253Amysql%2525252525252525253A%2525252525252525252F%2525252525252525252F127.0.0.1%2525252525252525253A3308%2525252525252525252Ftest%2525252525252525253F%25252525252525252B%2525252525252525252561%252525252525252525256c%252525252525252525256c%252525252525252525256f%2525252525252525252577%252525252525252525254c%252525252525252525256f%2525252525252525252561%2525252525252525252564%252525252525252525254c%252525252525252525256f%2525252525252525252563%2525252525252525252561%252525252525252525256c%2525252525252525252549%252525252525252525256e%2525252525252525252566%2525252525252525252569%252525252525252525256c%2525252525252525252565%2525252525252525253Dtrue%25252525252525252526%25252525252525252B%2525252525252525252561%252525252525252525256c%252525252525252525256c%252525252525252525256f%2525252525252525252577%2525252525252525252555%2525252525252525252572%252525252525252525256c%2525252525252525252549%252525252525252525256e%252525252525252525254c%252525252525252525256f%2525252525252525252563%2525252525252525252561%252525252525252525256c%2525252525252525252549%252525252525252525256e%2525252525252525252566%2525252525252525252569%252525252525252525256c%2525252525252525252565%2525252525252525253Dtrue%252525252525252526table%25252525252525253Da%252525252525252526username%25252525252525253Dfileread_%25252525252525252Fetc%25252525252525252Fpasswd%252525252525252526password%25252525252525253D123123%252525252525252526fetch_mode%25252525252525253DSINGLE"; + + SQLManager.validateJdbcUrl(jdbcConnection); + } + /** * Test fail if any exception is thrown therefore no assert */
Vulnerability mechanics
Generated 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.