VYPR
Critical severityNVD Advisory· Published Sep 21, 2025· Updated Sep 22, 2025

Deserialization Vulnerability in h2oai/h2o-3

CVE-2025-6544

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].

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.

PackageAffected versionsPatched versions
ai.h2o:h2o-coreMaven
<= 3.46.0.7
h2oPyPI
<= 3.46.0.7

Affected products

2
  • H2oai/H2o 3llm-fuzzy
    Range: <=3.46.0.8
  • h2oai/h2oai/h2o-3v5
    Range: unspecified

Patches

1
0298ee348f5c

Follow up on GH-16622 - handle also string with URL encoding characters (#16631)

https://github.com/h2oai/h2o-3Adam ValentaJul 15, 2025via ghsa
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

4

News mentions

0

No linked articles in our index yet.