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.

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.
What actually happened, in order
Disclosure to patch
- Public disclosure on X@q1uf3ng posts the
jsonArrayContainsSQL injection publicly, with no prior coordinated disclosure to GeoServer maintainers, describing a path to RCE via a privileged (sa) database account. - Exploitation attempts beginwatchTowr'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."
- Probing continues at volumeAttackers are "triggering errors and not proceeding further" — reconnaissance to build a list of vulnerable, internet-facing instances, not confirmed compromise yet.
- GeoServer ships fixed releasesVersions 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.
- Advisory formally publishedGitHub 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
- 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
- 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 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:
| CVE | What it was | CVSS | EPSS | KEV | Confirmed impact |
|---|---|---|---|---|---|
| CVE-2024-36401 | Unauthenticated RCE via unsafe XPath evaluation of property names, in every default GeoServer install | 9.8 | 99.79th pct | Yes | Exploited to build DDoS botnets, cryptomining botnets and residential-proxy networks |
| CVE-2025-58360 | Unauthenticated XXE via WMS GetMap | 8.2 | 99.18th pct | Yes | Added to KEV Nov 2026 |
| CVE-2023-25157 / 25158 | Unfiltered SQL injection across OGC filter functions (the bug this new flaw regresses) | 9.8 | up to 99.7th pct | No | Patched pre-emptively; workaround existed at the time |
| GHSA-mqjf-5f49-2fjh (this bug) | jsonArrayContains SQL injection to RCE | 9.8 | not yet scored | No CVE assigned | Probing/reconnaissance confirmed by watchTowr; no confirmed compromise reported as of writing |
Scores as of 2026-08-18live record →
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-postgispackage 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
- Upgrade to GeoServer 3.0.1, 2.28.5 or 2.27.6 (or bump the
gt-jdbc-postgisdependency directly to 35.1 / 34.5 / 33.6 if you embed GeoTools). There is no effective mitigation short of upgrading. - 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.
- Audit the database role GeoServer connects as. It should never be a PostgreSQL superuser, and
pg_execute_server_programshould be revoked unless something legitimately needs it. This limits the injection to data access rather than command execution. - 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
jsonArrayContainsfilter values and unexpected PostgreSQL errors in GeoServer logs around and after August 12. - 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 →