DEV Community

andreagomez
andreagomez

Posted on

OpenMetadata's JWT Filter Matched Paths by Substring, and a Semicolon Reopened the Door

OpenMetadata's JWT Filter Matched Paths by Substring, and a Semicolon Reopened the Door

Metadata platforms are inventories of everything else in the estate. OpenMetadata records where data lives, who owns it, which pipeline touches it and which service account connects to it, and it does so with credentials configured into the platform itself. Anything that reaches past its authentication filter reaches that inventory.

Where the check lived

OpenMetadata protected its API with a filter that compared the incoming request path against a list of endpoints excluded from JWT validation, which includes login and the handful of operations required before a token exists. The comparison was a substring test on the path. That single design decision produced CVE-2024-28255.
Two properties made it exploitable. First, substring matching does not distinguish a path from a path fragment, so a request whose path merely contains an allowed endpoint satisfies the check. Second, the servlet container normalises matrix parameters after the filter has run, so a path can carry something the filter reads one way and the router reads another. Writing the suffix as a matrix parameter produced a request that the filter classified as a login call and the application routed to an authenticated resource.
The published proof of concept appends the suffix v1 followed by an encoded users login fragment to a subscription validation endpoint, then supplies a Spring Expression Language payload in the trailing path segment. The expression is evaluated server-side. From there the chain reaches remote code execution, and Microsoft reported observing exploitation against Kubernetes environments for cryptomining from April 2024 onward.

The sibling that did not need a bypass

CVE-2024-28847 belongs to the same architectural defect without the path trickery. Several endpoints evaluated caller-influenced strings through SpEL, producing injection with a CVSS score around 8.8 across GHSL-2023-235 through GHSL-2023-237. The bypass in CVE-2024-28255 matters because it makes the injection unauthenticated. On its own, the injection matters because it existed on endpoints that authentication alone would not have made safe.
The pattern recurs. CVE-2026-26010 covers the ingestionPipelines API returning ingestion-bot JWTs, including credentials for Glue, Redshift and Postgres connections, to a caller with only read access, scored 7.6 as a privilege-management issue and fixed before 1.11.8. Different bug, same lesson: the metadata platform's own service accounts are the valuable object, and its API surface is broad enough that authorisation decisions get made inconsistently.

What an intruder gets

A single authenticated or bypassed request reaches an inventory that answers questions an attacker would otherwise spend weeks answering: where the production warehouse lives, which tables contain personal data, which pipelines run as which principal, and which users hold administrative roles. Lineage tells them which upstream feed to poison. Service configuration tells them which credential to reuse. None of that requires reading a single business record from the warehouse itself.

Judging the exposed population

A ZoomEye query run for this article on 2026-10-23 returned 1,261 results for the OpenMetadata application fingerprint. That figure describes fingerprinted reachable instances, not vulnerable ones, and it will include lab deployments and containers whose purpose is evaluation rather than production. It does not tell you which of them sit on an old release. The reason it still matters is the class of system being counted. A catalogue that maps every dataset and every pipeline in an estate is a targeting aid in a way that a wiki is not, and a four-figure population is enough for opportunistic scanning to pay for itself.

Testing your own deployment

  • Enumerate the excluded endpoints the filter recognises and confirm each comparison is an exact match on a normalised path, not a substring test or a prefix test.
  • Send a request that carries a matrix parameter in the path and confirm the filter and the router agree on the resulting resource. If they disagree, the bypass is present regardless of version string.
  • Search the codebase for expression evaluation applied to request-derived strings, and treat every occurrence as an injection sink rather than as configuration flexibility.
  • Audit what the ingestion bot token can reach. A component that can mint credentials for Glue, Redshift and Postgres is a high-value target and should be treated as one in network policy, not only in the API.
  • Rotate bot credentials if the instance ran an affected release while reachable from the internet, and review for pipelines created outside change control.

Fixing the boundary

Upgrade to a release past the affected versions, then verify the filter behaviour rather than trusting the version number, because the defect is a comparison method and comparison methods get reintroduced. Publish the metadata service behind an authenticating proxy, restrict outbound reachability from the container to the systems it actually queries, and log path normalisation decisions so that a mismatch between filter and router becomes visible rather than silent.

References

  • NVD record for CVE-2024-28255, JWT filter authentication bypass in OpenMetadata.
  • NVD record for CVE-2024-28847, SpEL injection in OpenMetadata.
  • GitHub Security Lab advisories GHSL-2023-235 through GHSL-2023-237.
  • NVD record for CVE-2026-26010, ingestion pipeline token disclosure, fixed before 1.11.8.
  • ZoomEye, measurement performed 2026-10-23 with the OpenMetadata application fingerprint; figure reflects reachable fingerprinted deployments, not confirmed vulnerable hosts.

Top comments (1)

Collapse
 
launchgatecheck profile image
Launch Gate •

The load-bearing detail is that the bug lives between two parsers, not in either one. The filter and the router each behaved "correctly" by their own rules; the vulnerability is the disagreement about what the path is. That is the same class as request smuggling between proxies, and it is why version-string checks miss it: any future change to normalization order reintroduces the mismatch even on a patched release.

The matrix-parameter probe you listed is the right fixture: send one request, then verify the filter and the router resolved the same resource. A cheap extension is to run the whole excluded-endpoint list against encoded slashes, trailing dots, and case variations, because each is a different place where "same path" can split.

Also appreciated the honest read on the ZoomEye count - fingerprinted reachability is not vulnerable population, and saying so makes the four-figure number more alarming, not less.