// HackTheBox
Analytics
Metabase pre-auth RCE (CVE-2023-38646) via H2 JDBC injection → env-leaked SSH creds → GameOver(lay) kernel privesc
Recon
Two ports, and the whole box hinges on what the web server is really hosting:
| Port | Service | Version |
|---|---|---|
| 22 | SSH | OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 |
| 80 | HTTP | nginx 1.18.0; redirects → analytical.htb (static landing) |
Port 80 doesn’t serve content for the raw IP — it redirects to the vhost
analytical.htb, so that name goes into /etc/hosts. analytical.htb itself is just
a static marketing landing page, but crawling its links turns up a second vhost,
data.analytical.htb, which is added to /etc/hosts alongside the first. That
subdomain answers with Metabase, an open-source business-intelligence dashboard.
A self-hosted analytics app on its own subdomain is exactly the kind of fat, versioned
target worth fingerprinting first — so that’s the thread to pull.
Foothold — CVE-2023-38646
1. Fingerprint Metabase and leak the setup-token
Metabase exposes an unauthenticated settings endpoint, /api/session/properties, that
dumps its public configuration as JSON — including the running version and, on a box
that was never fully initialised, the one-time setup-token used to gate the
first-run wizard:
$ curl -s http://data.analytical.htb/api/session/properties
Two facts fall out of that response:
- Version
v0.46.6(released 2023-06-29). setup-token249fa03d-fd94-4d5b-b94f-b4ebf3df681f— the token is non-null, which means the first-run setup flow was never completed and is still open.
Both matter for CVE-2023-38646, a pre-authentication RCE in Metabase. It was fixed in 0.46.6.1; 0.46.6 is squarely in the vulnerable range, and a live setup-token is the exact precondition the exploit needs.
2. Pre-auth RCE via H2 JDBC injection
Metabase ships with an embedded H2 database (a pure-Java SQL engine) for its own
sample data. The setup wizard lets you point Metabase at a database by supplying a JDBC
connection string, and POST /api/setup/validate “tests” that connection before any
account exists — using the leaked setup-token as the only credential. H2’s JDBC driver
allows a connection string to define a SQL trigger written in JavaScript, and that
trigger code runs on the server when the connection is opened. So a crafted connection
string turns “validate my database settings” into “execute my code”:
CREATE TRIGGER ... AS '//javascript ...' → command execution as the Metabase process.
The public exploit metabase_rce.py automates this against POST /api/setup/validate.
Two implementation details are easy to trip over:
- The full
detailsobject is required. The request’s outerdetailsfield must carry every sibling key the endpoint expects —is_on_demand,is_full_sync,is_sample,cache_ttl,refingerprint,auto_run_queries,schedules. Omit any and the server rejects the whole request with400 {"message":"Vector arg to map conj must be a pair"}before it ever reaches the vulnerable code. - The payload is Base64, space-padded. The reverse-shell command is Base64-encoded
and padded with spaces so it never ends in an
=character — an=would terminate the H2 connection string early and break the injection.
The trigger is wired to fire when Metabase runs its own SELECT against
INFORMATION_SCHEMA.TABLES during validation. The HTTP request comes back as a
400 SQL-syntax error, but that’s cosmetic — the trigger’s JavaScript has already
executed by then. The result is a reverse shell as uid=2000(metabase) inside the
container 71c5ea8a011e.
Lateral — env-leaked SSH credentials
The shell lands in a container, not on the host, so the next question is what the container knows that the host reuses. Metabase is configured through environment variables, and its own process environment hands over a set of credentials:
META_USER=metalytics
META_PASS=An4lytics_ds20223#
The variable names look Metabase-specific, but the values are reused as host SSH
credentials — the classic password-reuse pivot from a container out to the box it
runs on. They work directly over SSH, dropping a real session as
uid=1000(metalytics) and giving up user.txt:
$ ssh metalytics@10.129.229.224
Privilege Escalation — GameOver(lay)
On the host, uname -a reports Ubuntu 22.04.3 on kernel 6.2.0-25-generic. That
kernel is vulnerable to GameOver(lay) (CVE-2023-2640 / CVE-2023-3262), a flaw in
Ubuntu’s OverlayFS where file capabilities set on a file in an unprivileged user
namespace are copied up to the real filesystem — letting an unprivileged user stamp
privileged capabilities onto an executable.
The standard unshare -rm one-liner exploits it: create a new user + mount namespace,
build an OverlayFS mount, and set the cap_setuid capability on a copy of the ruby
binary. Because the capability survives the copy-up onto the host filesystem, that
ruby can then call Process::Sys.setuid(0) to become root:
$ ruby -e 'Process::Sys.setuid(0); exec "/bin/bash"'
That returns a root shell — read /root/root.txt to finish the box.