“It’s Log4Shell, Jim,” as Commander Spock never actually said, “But not as we know it.”
That’s the briefest summary we can come up with of the bug CVE-2021-42392, a security hole recently reported by researchers at software supply chain management company Jfrog.
This time, the bug isn’t in Apache’s beleagured Log4j toolkit, but can be found in a popular Java SQL server called the H2 Database Engine.
H2 isn’t like a traditional SQL system such as MySQL or Microsoft SQL server.
Although you can run H2 as a standalone server for other apps to connect into, its main claim to fame is its modest size and self-contained nature.
As a result, you can bundle the H2 SQL database code right into your own Java apps, and run your databases entirely in memory, with no need for separate server processes.
As with Log4j, of course, this means that you may have running instances of the H2 Database Engine code inside your organisation without realising it, if you use any apps or development components that themselves quietly include it.
JNDI back in the spotlight
Like the Log4Shell vulnerability, this one depends on abuse of the Java Naming and Directory Interface, better known as JNDI, which is an integral part of every standard Java installation.
JNDI is supposed to make it easier to identify and access useful resources across your network, including finding and fetching remotely stored software components using well-known search-and-discovery protocols such as LDAP (the Lightweight Directory Access Protocol).
As dangerous as this sounds, it’s important to remember that similar functionality can be coded into any software (compiled or interpreted, script or binary) that has network access, can download arbitrary data, and is able to turn that data into executable code of some sort. JNDI merely makes it very much easier to build distributed apps that find and load remote components. This sort of programmatic convenience sometimes improves security, because it’s often easier to audit and review code when it follows a well-documented path. But sometimes it reduces security, because it makes it easier to introduce unexpected side-effects by mistake. We saw this in Log4j, where “write out a text string to keep a record of data submitted by a remote user” could inadvertently turn into “download and run an untrusted program specified by a remote user”.
Fortunately, unlike Log4Shell, the CVE-2021-42392 bug can’t be triggered simply by emebdding booby-trapped text into queries that get sent to the H2 database engine.
Although Jfrog has documented several ways that cybercrimimals could, in theory, trick H2 into running arbitary remote code, the most likely attack path involves:
- An active H2 web-based console. This is a built-in web server that usually listens on TCP port 8082, and allows developers to interact with the H2 SQL backend while it’s running. If this port is blocked or the console is inactive then this avenue of attack won’t work.
- An H2 console listening on an external network interface. By default, the console only accepts connections from the computer it’s running on (
localhost, usually IP number
127.0.0.1in an IPv4 network). Unless this default is changed, attackers would need local access anyway before they could get at the H2 console.
According to H2, apps that embed the H2 engine directly into their code “are not externally exposed”, but as far as we can see this note refers only to the database engine itself when it’s not running as a SQL server, and not to the web console component.
Unfortunately, Jfrog notes:
We’ve observed that some third-party tools relying on the H2 database will run the H2 console exposed to remote clients by default. For example, the JHipster framework also exposes the H2 console, and by default sets the
webAllowOthers is the Java property used by H2 to decide whether to accept connections from external network interfaces.)
The default web console login page includes a form that allows users to specify how they want to connect to the database.
A malevolent user could use this form to request a JNDI lookup via LDAP, just like in a Log4Shell attack, in order to trick H2 into fetching and running a remote Java
.class file (a compiled Java program).
Although a treacherous URL used to launch an attack would be submitted in the the same login form that requests a username and password, Jfrog discovered that the JNDI lookup happens before the username and password are verified.
This means an attacker doesn’t need working credentials to exploit the vulnerability, so that the bug opens up what’s known as an unauthenticated remote code execution (RCE) hole, the most dangerous sort.
LEARN HOW JNDI AND LDAP COMBINE FOR REMOTE CODE EXECUTION
For a live demonstration of how JNDI can be maliciously combined with JDAP lookups to download and run untrusted remote code, watch this video:
If you can’t read the text in the video clearly here, try using Full Screen mode, or watch directly on YouTube. Click on the cog in the video player to speed up playback or to turn on subtitles.
What to do?
- If you have apps that use the H2 Database Engine, upgrade H2 to version 2.0.206.
At the time of writing, 2.0.206 (released 2022-01-04) is listed as the latest version, although the H2 changelog still lists 2.0.206 as “unreleased”, and doesn’t document CVE-2021-42392 as one of the issues fixed.
Jfrog, however, states that 2.0.206 includes a similar code change to the one that Apache used in the Log4j 2.17.0 update: H2 no longer allows JNDI to be used with any remote references.
This means, in theory, that attackers can no longer pull off the trick of saying “do a lookup, but use a network request that takes you to an untrusted exernal location so that we can manipulate the results”.
As far as we can see, the updated H2 Database Engine now only uses JNDI for what are essentially local Java function calls, so that remote code execution as an unexpected side-effect of using JNDI is no longer possible, neither by accident nor design.
- To find instances of the H2 code on your network, you can search for files called
The wildcard text denoted by
* should be of the form
X.Y.Z, representing the version number of H2 that’s in use – anything below 2.0.206 should be replaced with the latest version.