What you need to know about CVE-2021-42392

H2 Console in versions between 1.1.100 and 2.0.204 is vulnerable as it allows loading of custom classes from remote servers through JNDI, like the Log4Shell vulnerability. Let’s see in detail the issue and how this can affect our environment.

Starting the H2 Console

Firstly, let’s see the source of the issue. H2 ships with a Web Console which you can launch from the Command Line as follows:

$ java -cp h2-1.4.197.jar  org.h2.tools.Server -tcp -web
TCP server running at tcp://192.168.1.13:9092 (only local connections)
Web Console server running at http://192.168.1.13:8082 (only local connections)

By default, H2 Console doesn’t accept remote connections as you can see from the Login UI:

You can however enable remote access explicitly in two ways:

  1. Setting the property -webAllowOthers to true:
[-webAllowOthers]       Allow other computers to connect  

2. You can also allow remote access from the H2 Console Preferences:

By allowing connections from other computers, the H2 Console will be able to load custom classes from remote servers through JNDI. The root cause is pretty much the same as Log4j vulnerability How to fix Log4j CVE-2021-44228.

Specifically, the issue is in the JdbcUtils.getConnection method which takes as input the Driver class and the Database URL: https://github.com/h2database/h2database/blob/version-2.0.204/h2/src/main/org/h2/util/JdbcUtils.java#L317

If the Driver’s class is assignable to the javax.naming.Context class, the getConnection method instantiates an object from it and calls its lookup method:

else if (javax.naming.Context.class.isAssignableFrom(d)) {
                    // JNDI context
                    Context context = (Context) d.getDeclaredConstructor().newInstance();
                    DataSource ds = (DataSource) context.lookup(url);
                    if (StringUtils.isNullOrEmpty(user) && StringUtils.isNullOrEmpty(password)) {
                        return ds.getConnection();
                    }

Therefore, if you supply the Driver class such as javax.naming.InitialContext and a URL with the “ldap” protocol, that will lead to remote code execution:

Practical Impact of the issue

In practice, this issue impacts only H2 databases which are actively using the Web Console and enabled remote access.

If your application use the In-Memory H2 Database, like WildFly does, it’s not possible to access the In-Memory Database Console from another process. Unless you start a TCP server in the same process as the database was opened. Example:

// open the in-memory database within a VM
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:test");
conn.createStatement().execute("create table test(id int)");

// start a TCP server
// (either before or after opening the database)
Server server = Server.createTcpServer().start();

// .. use in embedded mode ..

// or use it from another process:
System.out.println("Server started and connection is open.");
System.out.println("URL: jdbc:h2:" + server.getURL() + "/mem:test");

// now start the H2 Console here or in another process using
// java org.h2.tools.Console -web -browser

System.out.println("Press [Enter] to stop.");

Also, as discussed in this tutorial – H2 Database Tutorial and expert Tips – you can change the URL of H2 ExampleDS DataSource to use TCP instead of In-Memory database. That will make you vulnerable to this issue.

Fixing the issue

A fix for H2 Database Console is available in the 2.0.206 release which denies loading of custom classes from remote servers through JNDI:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.0.206</version>
    <scope>test</scope>
</dependency>

You are therefore recommended to upgrade the H2 Database driver if you are allowing remote TCP Connections.

As mitigation fix, the H2 DB Site recommends (if you are not upgrading to the latest version) to enable Security Settings in the H2 Console application which otherwise runs unauthenticated:

<security-role>
    <role-name>admin</role-name>
</security-role>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>H2 Console</web-resource-name>
        <url-pattern>/console/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

References: https://jfrog.com/blog/the-jndi-strikes-back-unauthenticated-rce-in-h2-database-console/