CVE Tools
Back to blog

GeoServer's jsonArrayContains Zero-Day: Attackers Moved Faster Than the Patch

A researcher posted the SQL injection to X on August 12. WatchTowr saw exploitation attempts within hours. GeoServer shipped a fix two days later — and the mitigation that stopped its 2023 predecessor doesn't work this time.

A geospatial map interface cracking open to reveal raw SQL code leaking through the fracture, glowing database server racks visible behind the breach
A geospatial map interface cracking open to reveal raw SQL code leaking through the fracture, glowing database server racks visible behind the breach

On August 12, 2026 at 10:46 UTC, a researcher going by @q1uf3ng posted a message on X describing an unauthenticated SQL injection in GeoServer's jsonArrayContains filter function — and noted that, when the underlying database runs as the sa account, remote code execution follows "naturally." No CVE existed. No patch existed. GeoServer's maintainers hadn't been given a heads-up.

By the time GeoCat project owner Jody Garnett shipped fixed releases two days later, threat-intel firm watchTowr had already logged hundreds of exploitation attempts.

~hoursdisclosure to first exploitation attemptper watchTowr's Jake Knott
9.8CVSS 3.1 scoreAV:N/AC:L/PR:N/UI:N — no auth, no user click
2 daysdisclosure to patchAug 12 post → Aug 14 fixed releases
3rd timejsonArrayContains-class bug in GeoToolsregression of a 2023 fix

What actually happened, in order

Disclosure to patch

  1. Public disclosure on X
    @q1uf3ng posts the jsonArrayContains SQL injection publicly, with no prior coordinated disclosure to GeoServer maintainers, describing a path to RCE via a privileged (sa) database account.
  2. Exploitation attempts begin
    watchTowr's Jake Knott: "Within hours of public disclosure, we began observing exploitation attempts and have since recorded hundreds of attempts originating from a small number of source IP addresses."
  3. Probing continues at volume
    Attackers are "triggering errors and not proceeding further" — reconnaissance to build a list of vulnerable, internet-facing instances, not confirmed compromise yet.
  4. GeoServer ships fixed releases
    Versions 3.0.1, 2.28.5 and 2.27.6 released, bundling fixed GeoTools (35.1, 34.5, 33.6). Tracked as GHSA-mqjf-5f49-2fjh — no CVE identifier has been assigned as of this writing.
  5. Advisory formally published
    GitHub security advisory GHSA-mqjf-5f49-2fjh published by GeoCat's Jody Garnett, credited to reporters qquang, mrlihd, PhilipPhil and Quikko.

The bug: a value that was never supposed to be a string-within-a-string-within-a-query

jsonArrayContains is an OGC filter function GeoServer exposes so clients can query whether a JSON/JSONB column contains a given value — useful for anyone filtering geospatial features by tagged metadata. For PostGIS 12 and newer, GeoTools translates that filter into a PostgreSQL jsonb_path_exists() call. According to security researcher Melvin Lammerts (Hadrian), the vulnerable code — in FilterToSqlHelper.java, added when jsonArrayContains was introduced — builds that SQL with Java's String.format(), and the attacker-controlled value goes in without escaping.

That value sits inside PostgreSQL's jsonpath syntax, which itself sits inside a SQL string literal — a string within a string within a query. Because CQL (GeoServer's filter language) allows embedded quotes, an attacker can break out of the jsonpath string, close the SQL literal early, and inject arbitrary SQL. Every other GeoTools filter function uses parameterized queries; this one couldn't, because Postgres doesn't support bind parameters inside jsonpath expressions — so the escaping had to be done by hand, and wasn't.

Why WFS 1.0 is the easy path to RCE

Not every GeoServer service wraps the injected SQL the same way, and that difference decides how far an attacker can go:

Same bug, different blast radius by endpoint

WFS 2.0 / WMS GetMap
  • Wraps the filter in a derived-table subquery (to compute numberMatched)
  • Any injected semicolon lands inside that wrapper, not at the top level
  • GetMap is exploitable too, but requires closing three nested parentheses plus a geometry column — non-trivial
Structurally harder to stack a second statement
WFS 1.0
  • Doesn't require numberMatched, so GeoServer skips the wrapper
  • Injected filter lands directly in the top-level WHERE clause
  • A closing quote + semicolon stacks a second statement cleanly, e.g. a COPY ... TO PROGRAM call
The straightforward route to command execution

The detail that matters most for anyone who patched in 2023

GeoTools/GeoServer maintainers describe this bug plainly as a regression of CVE-2023-25157 / CVE-2023-25158 — the same class of unfiltered SQL injection across OGC filter functions, patched three years ago. The 2023 advisory offered a workaround for anyone who couldn't upgrade immediately: disable the PostGIS DataStore "encode functions" setting and enable "preparedStatements" to neutralize misuse of strEndsWith, strStartsWith, PropertyIsLike and FeatureId.

GeoServer's exploitation track record isn't hypothetical

watchTowr's Jake Knott framed the risk correctly: GeoServer has "a track record of being targeted and exploited at scale." Two prior GeoServer/GeoTools flaws are already in CISA's Known Exploited Vulnerabilities catalog, confirmed by our own graph data:

CVEWhat it wasCVSSEPSSKEVConfirmed impact
CVE-2024-36401Unauthenticated RCE via unsafe XPath evaluation of property names, in every default GeoServer install9.899.79th pctYesExploited to build DDoS botnets, cryptomining botnets and residential-proxy networks
CVE-2025-58360Unauthenticated XXE via WMS GetMap8.299.18th pctYesAdded to KEV Nov 2026
CVE-2023-25157 / 25158Unfiltered SQL injection across OGC filter functions (the bug this new flaw regresses)9.8up to 99.7th pctNoPatched pre-emptively; workaround existed at the time
GHSA-mqjf-5f49-2fjh (this bug)jsonArrayContains SQL injection to RCE9.8not yet scoredNo CVE assignedProbing/reconnaissance confirmed by watchTowr; no confirmed compromise reported as of writing

Who's exposed

  • GeoServer with a PostGIS datastore backed by PostgreSQL 12 or newer, on a column that is String or JSON/JSONB type
  • The vulnerable gt-jdbc-postgis package versions: 35.0, ≥34.0 (before 34.5), and ≥33.1 (before 33.6)
  • Any deployment exposing WFS 1.0 or WMS GetMap without an authenticating proxy in front — no GeoServer login is required to trigger the injection
  • Full RCE specifically requires the GeoServer DB account to hold superuser or pg_execute_server_program — but data-read/exfiltration works regardless of that privilege level

What to do now

  1. Upgrade to GeoServer 3.0.1, 2.28.5 or 2.27.6 (or bump the gt-jdbc-postgis dependency directly to 35.1 / 34.5 / 33.6 if you embed GeoTools). There is no effective mitigation short of upgrading.
  2. If you can't patch immediately, take WFS and WMS endpoints off the public internet — put them behind a VPN, reverse proxy, or IP allowlist. This is a stopgap, not a fix.
  3. Audit the database role GeoServer connects as. It should never be a PostgreSQL superuser, and pg_execute_server_program should be revoked unless something legitimately needs it. This limits the injection to data access rather than command execution.
  4. Check logs for probing now, not just after patching. watchTowr describes the current activity as attackers "triggering errors and comparing responses" to build target lists — look for malformed jsonArrayContains filter values and unexpected PostgreSQL errors in GeoServer logs around and after August 12.
  5. Don't assume the 2023 workaround (disabled encode functions + prepared statements) protects you here — the advisory explicitly says it doesn't.

Status as of publication 2026-08-18live record →