# Inside the Metabase SQLi: Exploited in the Wild

Unauthenticated attacker injects a user-id object with HoneySQL :raw into Metabase's /api/session/reset_password, achieving blind SQL injection on Cloud and self-hosted instances.

- Payout: Real-world hack
- Severity: critical
- Program: Metabase
- Credited: Wiz Research
- Disclosed: 2026-08-10
- CVE: CVE-2026-72898
- Write-up quality: 8/10
- Original: https://www.wiz.io/blog/inside-the-metabase-sqli-exploited-in-the-wild
- Library page: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-inside-the-metabase-sqli-exploited-in-the-wild
- Tags: domain:web3 impact:data-leak impact:mass-exploit kind:incident platform:threat-intel sev:critical tech:rest technique:incomplete-fix technique:reverse-engineering technique:source-review vuln:sqli

**Target:** /api/session/reset_password JSON body on Metabase server (JAR/Clojure)
**Root cause:** The reset_password handler merges the request map with the auth result; on failed auth no user-id is returned, so an attacker-supplied user-id survives. JSON keywordization turns {"user-id":{"raw":"SQL"}} into a Clojure map whose :raw key HoneySQL compiles as literal unparameterized SQL.
**Steps:**
1. Download vulnerable v0.58.22 and patched v0.58.24 JARs and diff contents
2. Decompile the Clojure class and correlate bytecode diff to source; patch adds pos-int? check on user-id
3. Note user-id is not a documented reset_password parameter, so it must be an extra JSON key that isn't stripped
4. Send `curl -X POST https://TARGET/api/session/reset_password -H 'Content-Type: application/json' -d '{"token":"x","password":"y","user-id":{"raw":"SQL_PLACEHOLDER"}}'`
5. Replace SQL_PLACEHOLDER with DB-specific blind SQLi payload (H2/Postgres/MySQL)
6. Confirm version via /api/session/properties
**How found:** Patch was release-only, not in public GitHub. Wiz diffed vulnerable vs patched JARs, decompiled Clojure, correlated bytecode diff to source, and spotted the pos-int? guard on user-id as the smoking gun; since user-id isn't a documented parameter, they inferred an extra JSON key surviving a merge, then reconstructed the HoneySQL :raw path.
**Exploitation:** Unauthenticated POST to /api/session/reset_password with a bogus token/password plus a user-id object containing a HoneySQL :raw SQL string; the merge preserves the attacker key and t2/select-one passes it to HoneySQL, executing arbitrary SQL against the backing database (blind). Used as a zero-day against Metabase Cloud, with downstream victims (Framework, Tally, n8n, Kilo Code, ChecklyHQ) disclosing impact.
**Escalation:** Blind SQLi against the Metabase application database enables data exfiltration; internet-exposed self-hosted instances (~25% of deployed) broaden exposure.
**Insight:** When a patch adds a type/range guard on a field that isn't a documented API parameter, suspect mass-assignment via map merge: look for `(merge request ...)` patterns where attacker keys survive failed auth, and for ORM escape hatches like HoneySQL :raw that turn a nested JSON object into unparameterized SQL.
