4 min read
‧ 4 min read
On August 3, 2026, one of our Metabase Cloud customers reported that an API key had been created outside of normal working hours.
We investigated and noticed an anomalous access pattern - a single IP had created an api key on two different customer instances, in the same sequence of endpoints. We identified the initial endpoint that started the attack chain and put in a network block across our fleet of customer instances.
We investigated and determined that a valid session ID was being created, and, through reverse engineering, we identified an unknown (“Zero-day”) vulnerability that allowed the threat actor to generate an active session. We created a patch and immediately pushed it to our customer cloud instances.
We searched our logs and began to investigate any customer impact. We also engaged a 3rd party incident response and forensics firm.
We began preparing a public release and, in our investigation, found another few derivative vulnerabilities from the same primitive and added fixes for those vulnerabilities to the release.
We packaged these fixes and released the patched versions on August 6th.
All Metabase installations running versions 0.58 and later were vulnerable.
Under 3% of our cloud customers were compromised as a result of the vulnerability before patching, as were some open source users and self-hosted customers with publicly accessible Metabase instances
The vulnerability was a chained attack that took advantage of how 4 layers of our codebase interacted.
The starting point for the attack was our password reset url (/reset-password)
This URL accepted a request-body:
`[:map [:token ms/NonBlankString] [:password ms/ValidPassword]]`
The first link in this chain was that fact that our API type annotations in Malli weren’t “closed”; the type annotations only verified that the required keys were present and of the correct type. Additional keys wouldn’t cause a validation error.
The second step was that an auth system refactor on Nov 11, 2025 changed parameter handling from only passing through the token and password keys to passing on the entire body.
The third step is that in the entry to the Auth flow, if a user ID specified, then that user’s record is looked up by ID by our ORM, Toucan2. This was done with Toucan2’s select-one function, with the “ID” of the user being the value that was passing in from password reset url.
Lastly, toucan2 used HoneySql to generate the query.
If the filter predicate included a “raw” key, honeysql treated this as raw sql to insert unchanged.
By submitting a request body like
{ "token": "any",
"password": "pass",
"user_id": {"raw": "1);INSERT INTO ...; --"} }
an attacker could inject SQL.
That actual attack used this to insert a record into our session table, with a known user_id of 1 (the first user in a Metabase instance, always initially created as an “admin”), and then using that session to browse for data, and create an API key to allow bulk table downloads.
While this attack can be executed in a number ways, the pattern we saw was
HTTP POST /api/session/password-reset 400
HTTP GET /api/user/current 200
We guarded against this attack at three different layers by:
Only passed through the token and password fields from the api handler
Rejected abusable keys in the entry to the auth flow
Threw exceptions when unexpected keys were encountered in utility macros
Due to the complexity of this attack, the required knowledge of our codebase, and the behavior of two nested dependencies (Toucan2 and Honey SQL), we believe this attack required significant LLM capabilities.
Early requests used user-agent strings that suggest overriding guardrails on an LLM: “metabase-postgres-admin-session-lab-verifier/1”, “metabase-read-only-sqli-verifier/1“, before rotating between 25 variants similar to “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0” “Safari/537.36 Edg/146.0.0.0 Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0”.
Given the extremely rapid increase in capabilities in LLM code scanning, and its ability to weave together deep attack chains, we are hardening Metabase across the layers rather than stopping at fixing this one vulnerability.
This hardening is happening at many layers of our codebase, including:
We released the first batch of this hardening in version 63.13, and will be following up with another hardening release before returning to our normal development cadence.
We’re also engaging with external researchers (another hat tip to Ophion Security and the Anthropic red team) and continuing to scan our codebase with a greater variety of models and approaches.