Skip to content

CVE-2026-41042: Verified Reproduction

CVE-2026-41042: Apache Gravitino unauthenticated H2 JDBC URL injection via testConnection API

CVE-2026-41042 is verified against apache/gravitino · github. Affected versions: Apache Gravitino before 1.2.1. Fixed in 1.2.1. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00276.

REPRO-2026-00276 apache/gravitino · github Jul 9, 2026 CVE entry .txt
Severity
CRITICAL
CVSS
9.1
Confidence
HIGH
Reproduced in
22m 47s
Tool calls
308
Spend
$5.32
01 · Overview

What Is CVE-2026-41042?

CVE-2026-41042 is a low-severity vulnerability in Apache Gravitino before 1.2.1 in which an unauthenticated caller can supply a malicious H2 JDBC URL through the testConnection API (or catalog creation) that executes arbitrary Java code on the server via H2's INIT parameter. Pruva reproduced it (reproduction REPRO-2026-00276).

02 · Severity & CVSS

CVE-2026-41042 Severity & CVSS Score

CVE-2026-41042 is rated critical severity, with a CVSS base score of 9.1 out of 10.

CRITICAL threat level
9.1 / 10 CVSS base
Weakness CWE-20 — Improper Input Validation

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected apache/gravitino Versions

apache/gravitino · github versions Apache Gravitino before 1.2.1 are affected.

How to Reproduce CVE-2026-41042

$ pruva-verify REPRO-2026-00276
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00276/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-41042

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

malicious H2 JDBC URL (jdbc:h2:mem:...;INIT=CREATE ALIAS ... ProcessBuilder ...) supplied unauthenticated via testConnection API catalog properties

Attack chain
  1. POST /api/metalakes/{metalake}/catalogs/testConnection
  2. CatalogManager.testConnection
  3. BaseCatalog.ops()
  4. JdbcCatalogOperations.initialize
  5. DataSourceUtils.createDataSource
  6. H2 driver opens connection and runs INIT
  7. CREATE ALIAS compiles+executes Java
  8. ProcessBuilder runs OS command
Runnable proof: reproduction_steps.sh
Captured evidence: fixed attempt 1fixed attempt 2fixed server
How the agent worked 712 events · 308 tool calls · 23 min
23 minDuration
308Tool calls
194Reasoning steps
712Events
Agent activity over 23 min
Support
14
Hypothesis
2
Repro
333
Judge
23
Variant
185
Coding
150
0:0022:47

Root Cause and Exploit Chain for CVE-2026-41042

Versions: Apache Gravitino before 1.2.1 (confirmed on the

Apache Gravitino (before 1.2.1) exposes an unauthenticated REST endpoint POST /api/metalakes/{metalake}/catalogs/testConnection that lets a caller supply an arbitrary JDBC URL for a catalog connection test. Because no validation rejected H2 JDBC URLs or the H2 driver class, an attacker could supply a malicious jdbc:h2:...;INIT=... URL. H2's INIT connection parameter runs arbitrary SQL when the database is first opened, and H2's CREATE ALIAS ... AS '<java source>' compiles and executes arbitrary Java code inside the Gravitino server JVM. By combining CREATE ALIAS with Runtime/ProcessBuilder calls, an unauthenticated remote caller achieves arbitrary operating-system command execution on the server.

  • Package/component affected: org.apache.gravitino:gravitino-catalog-jdbc-common (DataSourceUtils.createDataSource), reached through the testConnection REST endpoint and the JDBC catalog operations (JdbcCatalogOperations.initialize).
  • Affected versions: Apache Gravitino before 1.2.1 (confirmed on the official 1.2.0 binary distribution; H2 1.4.200 is bundled).
  • Risk level / consequences: Remote code execution. The default server configuration uses the SimpleAuthenticator (accepts anonymous requests) and has gravitino.authorization.enable = false, so the endpoint is reachable without any credentials. The H2 driver is always on the server classpath (it is the default entity-store backend) and is treated as a "shared" class by the catalog IsolatedClassLoader, so attacker-supplied H2 URLs resolve to the bundled driver even for non-H2 catalog providers.

Impact Parity

  • Disclosed/claimed maximum impact: Remote code execution — "executes arbitrary Java code on the server via H2's INIT parameter", expected impact code_execution.
  • Reproduced impact from this run: Arbitrary OS command execution confirmed end-to-end through the unauthenticated REST API. The malicious testConnection call returned HTTP 200 / {"code":0} and a marker file containing the output of the id command (uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)) was written by a ProcessBuilder started from H2-compiled Java code running in the server JVM.
  • Parity: full.
  • Not demonstrated: N/A — the full code-execution chain was demonstrated.

Root Cause

DataSourceUtils.createDataSource(JdbcConfig) is the single entry point for all user-facing catalog JDBC connections. In the vulnerable version it performed no allow-listing of JDBC drivers/URLs: it passed the caller- controlled jdbc-url and jdbc-driver straight into a DBCP BasicDataSource. The shared validator JdbcUrlUtils.validateJdbcConfig only inspects MySQL/MariaDB/PostgreSQL URLs for unsafe parameters and intentionally does not touch H2 (H2 is the legitimate embedded entity-store backend). Consequently an H2 URL reached the driver untouched.

The call chain triggered by testConnection is:

CatalogOperations.testConnection (REST, @Path "testConnection")
  -> CatalogManager.testConnection
    -> CatalogWrapper.doWithCatalogOps
      -> BaseCatalog.ops()  (lazy initialize)
        -> JdbcCatalogOperations.initialize(conf, ...)
          -> DataSourceUtils.createDataSource(jdbcConfig)   <-- vulnerable
            -> BasicDataSource (url=jdbc:h2:mem:...;INIT=..., driver=org.h2.Driver)

When the pool opens its first connection (during JdbcCatalogOperations.testConnection -> databaseOperation.listDatabases()), H2 processes the INIT parameter. The payload INIT=CREATE ALIAS EXEC AS 'String f() throws Exception { new ProcessBuilder(...).start().waitFor(); ... }'\;CALL EXEC() compiles the Java source via the JDK compiler and invokes it, executing the attacker's OS command inside the server process.

Fix commit: 5daabcd0e8ddc96e25bd6c6ce7b153cdb311c3f2 ("[MINOR] fix(catalog): block H2 JDBC URL and driver in catalog datasource creation (#10801)"), cherry-picked to the 1.2 release branch as 84d3de9c7 and shipped in Gravitino 1.2.1. The fix adds a case-insensitive block in DataSourceUtils.createDataSource() that rejects any URL whose decoded form starts with jdbc:h2 and any driver class starting with org.h2., throwing GravitinoRuntimeException before the datasource is created.

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh (self-contained, idempotent, run twice consecutively — both runs pass with exit 0).
  2. What it does:
    • Installs OpenJDK 17 if needed.
    • Downloads/reuses the official Apache Gravitino binary distributions gravitino-1.2.0-bin.tar.gz (vulnerable) and gravitino-1.2.1-bin.tar.gz (fixed) from the GitHub release CDN (fallback: Apache archive).
    • For each version: extracts a clean copy, disables auxiliary services, starts the real gravitino.sh server, waits for the /api/version healthcheck, creates a metalake, and sends an unauthenticated POST /api/metalakes/test_ml/catalogs/testConnection with provider=jdbc-postgresql, jdbc-driver=org.h2.Driver, and a malicious jdbc-url whose INIT runs CREATE ALIAS + ProcessBuilder to execute id and write its output to a marker file. Two attempts are made per version (each attempt uses a fresh H2 in-memory DB name because H2 only runs INIT when a database is first created).
    • Captures per-attempt request/response logs, server logs, version JSON, and the RCE marker file under bundle/logs/.
    • Writes bundle/repro/runtime_manifest.json.
  3. Expected evidence:
    • Vulnerable 1.2.0: testConnection returns HTTP 200 {"code":0} and the marker file is created containing id output → RCE.
    • Fixed 1.2.1: testConnection returns HTTP 500 "H2 JDBC URL is not allowed in catalog configuration" (thrown at DataSourceUtils.createDataSource:54) and no marker file is created.

Evidence

  • bundle/logs/reproduction_steps.log — orchestrator log with verdict.
  • bundle/logs/vuln_version.json1.2.0, gitCommit 1c1665f0....
  • bundle/logs/fixed_version.json1.2.1, gitCommit e88bffcfc....
  • bundle/logs/vuln_attempt_1.log / vuln_attempt_2.logtestConnection_status: 200, testConnection_body: {"code":0}, marker_exists: True, marker_content: uid=1000(vscode) ....
  • bundle/logs/rce_marker_vuln_latest.txt — the file written by the attacker's OS command inside the server JVM.
  • bundle/logs/fixed_attempt_1.log / fixed_attempt_2.logtestConnection_status: 500, message "H2 JDBC URL is not allowed in catalog configuration", stack at DataSourceUtils.createDataSource(DataSourceUtils.java:54), marker_exists: False.
  • bundle/logs/vuln_server.log / fixed_server.log — server-side logs.
  • bundle/repro/runtime_manifest.json — runtime evidence manifest.

Key excerpts:

# Vulnerable 1.2.0 (unauthenticated)
vuln_a1 testConnection_status: 200
vuln_a1 testConnection_body: {"code":0}
vuln_a1 marker_exists: True
vuln_a1 marker_content: uid=1000(vscode) gid=1000(vscode) groups=1000(vscode)

# Fixed 1.2.1 (unauthenticated)
fixed_a1 testConnection_status: 500
fixed_a1 testConnection_body: {"code":1002,"type":"RuntimeException",
  "message":"H2 JDBC URL is not allowed in catalog configuration",
  "stack":["...DataSourceUtils.createDataSource(DataSourceUtils.java:54)",
           "...JdbcCatalogOperations.initialize(JdbcCatalogOperations.java:175)",...]}
fixed_a1 marker_exists: False

Environment: OpenJDK 17.0.19, official Apache Gravitino binary tarballs, H2 1.4.200 (bundled), Jetty 9, commons-dbcp2 2.11.0, default server config (SimpleAuthenticator, authorization disabled, H2 entity-store backend).

Recommendations / Next Steps

  • Upgrade to Apache Gravitino 1.2.1 or later, which blocks H2 JDBC URLs and the org.h2. driver class in DataSourceUtils.createDataSource().
  • Consider a positive allow-list of permitted JDBC URL schemes/drivers in catalog configuration rather than only blocking H2, and apply the same check at catalog creation time (not only testConnection).
  • In production deployments, enable authentication/authorization and use MySQL (not H2) for the entity store; restrict network exposure of the testConnection endpoint.
  • Add regression tests that assert a jdbc:h2:...;INIT=... URL and org.h2.Driver are rejected through the full REST testConnection path.

Additional Notes

  • Idempotency: confirmed — the script was run twice consecutively; both runs exited 0 with identical verdicts (vulnerable RCE confirmed on both attempts, fixed blocked on both attempts). Each run extracts into fresh directories and uses a unique H2 in-memory DB name per attempt.
  • Payload detail: every ; inside the H2 INIT value (including Java statement terminators) must be escaped as \;, because H2 uses ; as the URL parameter separator; unescaped ; truncates the URL and the INIT never runs. The jdbc-database property must also be supplied for the jdbc-postgresql provider (PostgreSqlSchemaOperations.initialize reads it before the test connection is opened).
  • The exploit does not require the catalog provider to be H2-aware: the H2 driver is a "shared" class (!isCatalogClass) so the catalog IsolatedClassLoader delegates org.h2.Driver to the server classloader, which always has it.

CVE-2026-41042 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:49
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-41042 · REPRO-20
0:02
0:03
web search
0:05
web search
0:07
0:09
0:11
web search
0:23
0:23
extract_facts
no facts extracted
0:24
0:24
0:24
supportrepro
0:37
0:39
0:39
0:39
0:40
0:40
0:40
0:42
0:42
0:42
0:43
0:43
0:43
0:43
0:45
0:47
web search
0:47
0:49
08 · How to Fix

How to Fix CVE-2026-41042

Upgrade apache/gravitino · github to 1.2.1 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-41042 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-41042

How does the CVE-2026-41042 exploit work?

An unauthenticated caller sends a JDBC URL to the REST endpoint POST /api/metalakes/{metalake}/catalogs/testConnection. Because the URL isn't validated, an H2 URL with an INIT=CREATE ALIAS ... AS '<java source>' clause compiles and executes arbitrary Java code inside the Gravitino server JVM at connection time, which can then invoke Runtime/ProcessBuilder to run OS commands.

Which Apache Gravitino versions are affected by CVE-2026-41042, and where is it fixed?

Apache Gravitino before 1.2.1 is affected. It is fixed in 1.2.1 — upgrade to 1.2.1 or later.

How severe is CVE-2026-41042?

The record rates this as low severity, though the reachable code path (an unauthenticated REST endpoint reaching H2's INIT/CREATE ALIAS mechanism) results in arbitrary code execution in the server JVM, per the technical analysis.

How can I reproduce CVE-2026-41042?

Download the verified script from this page and run it in an isolated environment against Apache Gravitino before 1.2.1. It submits a crafted H2 JDBC URL with an INIT parameter to the testConnection API and shows the resulting Java/OS code execution inside the Gravitino server.
11 · References

References for CVE-2026-41042

Authoritative sources for CVE-2026-41042 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.