Metabase Zero Day: 16 of 35 Swiss Instances Run an Unfixed Build

A maximum severity SQL injection in Metabase was exploited three days before a fix existed. We read the running version of every Swiss instance we could find. 16 of 35 are missing the fix, and 16 more were out of range only because they run a branch older than the flaw reaches.

threat-intelsql-injectionexposureswitzerland

On 3 August someone started using a flaw in Metabase that nobody had a fix for. The vendor published the patch on 6 August, rated 10 out of 10, and the following day the first breached companies began telling their customers what had been taken. We requested every Metabase instance an internet exposure index returned for Swiss networks this morning, four days after the fix shipped, and read the running version from each one that answered. Of the 35 instances we confirmed, 16 are running a build without the fix.

This one has no CVE number, the identifier almost every security tool sorts by. That is worth saying up front, because it removes most of the signals people use to judge whether a flaw matters. With no CVE there is no published probability score for it being exploited, and it will never appear in the US government catalogue of vulnerabilities known to be under attack, because that catalogue is indexed by CVE number. We rechecked that catalogue this morning: the only Metabase entry in it is a different flaw from 2021. A vulnerability that was exploited before its own patch existed is therefore invisible to every feed that filters on CVE.

What the bug does

Metabase is a business intelligence tool. You point it at your databases, it stores the connection credentials for each one, and it draws the dashboards your colleagues look at. It is published to the internet in most deployments, because that is how people reach the dashboards.

The flaw sits in the password reset endpoint, POST /api/session/reset_password. That endpoint has to work for people who are locked out, so it accepts requests from anyone. Input arriving there was placed into a database query without being separated from the query itself. A crafted request therefore runs the sender’s own SQL against the Metabase application database.

That database holds the user account table. Writing to it makes the sender an administrator.

The interesting part is what an administrator gets. Not a shell, and not the contents of Metabase, which is mostly saved questions and dashboard layouts. What they get is the stored connection credentials for every database Metabase reports on. A reporting tool is, by design, the one system in the building that holds a working login to everything worth reporting on. Compromising it is a way of collecting all of them in one visit.

The vendor’s own severity scoring records the same point formally: the damage does not stop at Metabase. No account, no password, and no user interaction are required to start.

Affected versions are 0.58 through 0.63 in the open source numbering, and 1.58 through 1.63 for Enterprise, which uses the same branch numbers with a different leading digit. Anything below 0.58 is not affected. The fixed releases:

Branch Fixed in
0.58 0.58.24
0.59 0.59.21
0.60 0.60.17
0.61 0.61.11
0.62 0.62.9
0.63 0.63.5

One distinction matters for everything that follows. The attacks that were observed, and every breach disclosed so far, landed on the vendor’s own hosted service, which the vendor patched for its customers automatically. Installations that companies run themselves carry the same flaw with nobody to upgrade them, and those are what we measured.

How we counted

We searched an internet exposure index for Metabase on Swiss networks and got 59 records. Because the index returns one record per service, those collapsed to 52 unique hosts.

Then we asked all 52 directly, rather than trusting the index. This matters more than it sounds. Of the 52, 14 did not answer at all when we requested them, and 3 answered but turned out not to be Metabase: a network appliance interface, a different analytics vendor’s login page, and a software company’s marketing site, each of which happens to contain the word Metabase somewhere in its HTML. That leaves 35 confirmed instances, and a third of what the index offered was either gone or never the product in the first place.

We read the version live from 34 of those 35, which is 97 percent coverage. The one gap is honest and worth stating: it answered with the Metabase page title and session cookie, and returned “Metabase is still initializing. Please sit tight…” on the version endpoint. It was restarting. We counted it in the estate and put it in no version bucket rather than guessing.

Why the version has to be read live

Metabase does not tell you its version passively. It is not in the page title, not in the HTML, and not in any response header. The Server header reports the embedded web server, which tracks the Metabase release loosely and cannot resolve a patch level at all. If you pull Metabase exposure figures from an index’s cached version field, the numbers you get describe the bundled web server, not the product.

What Metabase does expose is a public settings document at GET /api/session/properties, served without authentication because the login screen renders from it. It carries the running build:

"version": {"date": "2026-08-05", "tag": "v0.63.5", "hash": "1dca717"}

One GET, read one field. It is not the vulnerable endpoint, and it is the same request your own monitoring could make.

That build date turned out to be useful for a second reason. It gave us an independent way to reach the same verdict: the fixed builds are stamped 5 and 6 August, so a build stamped earlier cannot contain the fix regardless of how you read its version number. We ran both checks against every instance. They disagree on none of them.

The numbering needed checking before we trusted it. Live reads returned both v0.63.5 and v0.63.1.8, and a three part release is not the same kind of thing as a four part one. Pulling the vendor’s own release history, 800 releases of it, resolved the question: modern branches publish three part releases, and four part builds are hotfixes on top of a release that carry their own date. So 0.63.1.8 sits below 0.63.5, and 0.63.5.2 sits above it.

The same check turned up something we did not go looking for. None of the six fixed releases appears in the vendor’s public release list on GitHub. The fixes exist, and one Swiss instance is running one of them, but an administrator watching that page for a new release would not have seen them arrive.

What the versions say

Result Instances
Running a build without the fix 16
Running a fixed build 2
On a branch below 0.58, not affected 16
Restarting, version unreadable 1

There is a tempting ratio here that is wrong, so it is worth spelling out. “Only 2 of 35 have patched” reads as though 33 organisations ignored a maximum severity flaw. They did not. Of those 35, 16 run a branch below 0.58, for which no fix for this flaw was ever issued, because their branch was never vulnerable to it. They had nothing to install.

The honest denominator is the 18 instances on an affected branch. Of those, 16 do not carry the fix and 2 do.

The number we did not expect

The 16 instances that are not affected are not the good news they look like.

They are outside the range of this flaw because they run a branch older than 0.58, not because of their age as such: 5 of the 16 carry a build less than a year old. The other 11 do not. Of the 34 instances we could read, 11 run a build more than a year old and 4 are more than two years old, and every one of those 11 sits in this group. The oldest was built on 31 July 2020, six years ago, and it is still answering requests on the Swiss internet today.

Being that far behind is not a security posture. Those instances are missing every fix issued for the product since their branch was current; this particular flaw simply is not among them. It happened to be the wrong door. The next one will not be.

There is a second finding in the same direction. The vendor’s suggested workaround for anyone who cannot upgrade immediately is to block /api/session/reset_password at the reverse proxy, the server that normally sits in front of an application and filters what reaches it. Of the 16 unpatched instances, 7 answered on the product’s default port with the product’s own web server replying and no proxy announcing itself in front. Those 7 would have to put one there first.

Geographically the estate concentrates in Geneva, with 15 instances, and Zurich with 11.

Reading it yourself

If you run Metabase, this takes a minute.

curl -s https://your-metabase.example.ch/api/session/properties \
  | python3 -c "import json,sys; print(json.load(sys.stdin)['version'])"

Compare the tag to the fixed release for your branch in the table above. Below it, upgrade. If you are exactly one release short of the fixed number, the advisory’s own ranges do not settle it either way, so use the build date in the same response: anything stamped before 5 August 2026 predates the fix.

What to do

  1. Upgrade to the fixed release for your branch.
  2. If you cannot upgrade today, block /api/session/reset_password at your reverse proxy. Check first that you have one.
  3. If the endpoint was reachable before you patched, assume administrator access was possible. Clear active sessions from the core_session table, review API keys and administrator accounts for entries nobody recognises, and rotate the credentials of every database the instance connects to.
  4. Search your logs for the published exploitation pattern: a POST to /api/session/reset_password returning 400, followed by a successful GET to /api/user/current from the same source. That pair means the injection worked and a session was established.

Step 3 is the one that gets skipped, and it is the one that matters most. The flaw hands over the stored credentials for every database behind the tool. Patching the application closes the door. It does not change the locks on everything the attacker already walked through.


We measure Swiss attack surface and publish what we find. This measurement was taken on 10 August 2026 by requesting every host directly and reading the version each one reported. No exploit code was sent to any host, and the vulnerable endpoint was never contacted.