Hundreds. That is how many exploitation attempts watchTowr recorded against a new GeoServer SQL injection bug within hours of its public disclosure on August 12, 2026 - before the GeoServer project had a patch ready, and before the bug even carried a CVE number. By the time fixed builds shipped two days later, on August 14, attackers already had a head start most defenders did not know they needed.
The bug lives in a filter function called jsonArrayContains, and it is not a new category of mistake. GeoServer's maintainers fixed this exact category once before, in February 2023, under a different function name. That fix closed one door. It left the frame.
If you do not run GeoServer, or you run it without a PostGIS backend, skip this one. If you work in a municipal GIS department, a utility, a transportation authority, an environmental agency, a university research group, or you contract for any of those, keep reading. GeoServer renders and serves the geospatial data behind property records, flood maps, transit routes, and utility infrastructure inventories, and it shows up disproportionately often in exactly the small, under-resourced IT shops - county governments, regional water authorities, small engineering and surveying firms - that do not have an application security team watching GitHub advisories on a Tuesday afternoon.
These are also the organizations least able to move on a two-day patch cycle. A county GIS server was likely stood up years ago by a contractor who has since moved on, funded through a capital line item that assumes updates happen annually if at all, and left internet-facing because the property-records portal it feeds needs to be reachable by the public. That combination - old deployment, thin ownership, public exposure - is precisely the profile watchTowr's scanning traffic is most likely to find first.
The 2023 fix everyone assumed closed this
In February 2023, GeoServer's GeoTools library shipped a fix for CVE-2023-25158, an unauthenticated SQL injection reachable through OGC filter expressions. The recommended remediation at the time was two-part: enable prepared statements on the datastore connection, and disable a set of encode functions that built raw SQL fragments from user input. Thousands of GeoServer operators applied that guidance and reasonably considered the filter-injection problem solved.
It was solved for the code paths that existed in 2023. It was not solved for the pattern - a filter function that accepts a user-controlled value and writes it into a generated SQL string. Every new filter function added since then had to reimplement escaping correctly on its own, with no structural guardrail forcing it. jsonArrayContains did not.
That is a design problem, not a one-line typo. GeoServer's OGC filter engine translates a standardized query language into whatever SQL dialect the backing datastore speaks, and each filter function owns its own translation logic. A centralized query builder that always parameterizes values would make this class of bug structurally hard to reintroduce. A per-function translation layer means the safety of the whole system depends on every contributor remembering the 2023 lesson, forever, one function at a time. jsonArrayContains was added later and nobody carried the lesson forward into it.
What actually broke this time
The GitHub security advisory for GHSA-mqjf-5f49-2fjh, published by the GeoTools maintainers, is direct about it: jsonArrayContains(<column>, <pointer>, <value>) "writes the value parameter into generated SQL without escaping" when the backend is PostGIS 12 or newer against a String or JSON field. An attacker who can submit an OGC filter - which on most GeoServer deployments means anyone who can reach the WFS or WMS endpoints, authenticated or not - can substitute arbitrary SQL for that value. CVSS 9.8: network attack vector, no privileges required, no user interaction.
Two details make this worse than a routine SQL injection. First, the advisory states plainly that "the previously recommended approach of enabling prepared statements while disabling encode functions does not address this particular vulnerability" - the 2023 mitigation that many operators are still relying on today does nothing against this one. Second, on deployments backed by an H2 database rather than PostGIS, the same class of unescaped input has historically been pushed further, into full remote code execution, because H2 supports registering arbitrary Java functions from SQL. GeoServer's maintainers, credited reporters qquang, mrlihd, PhilipPhil, and Quikko, and remediation lead Jody Garnett shipped patched releases on August 14: GeoServer 3.0.1, 2.28.5, and 2.27.6, alongside GeoTools gt-jdbc-postgis 33.6, 34.5, and 35.1.
The facts, in one place
- Affected:
org.geotools:gt-jdbc-postgisversions 33.1 through 35.0, shipped inside GeoServer with a PostGIS datastore on PostGIS 12+. - Patched: GeoTools 33.6, 34.5, 35.1 - bundled in GeoServer 2.27.6, 2.28.5, and 3.0.1.
- Disclosure: August 12, 2026, 10:46 UTC, posted publicly to X by a researcher using the handle @q1uf3ng, ahead of any coordinated vendor timeline.
- CVE status at publication: none assigned. The only tracking identifier is the GitHub Security Advisory ID.
A disclosure with no runway
GeoServer's own release notes for the August 14 patch acknowledge that coordinated vulnerability disclosure procedures were not followed this time - the bug went public before the maintainers had a fix staged, which is the opposite of how a responsible disclosure timeline is supposed to work. That gap is exactly what watchTowr watched attackers exploit. Researcher Jake Knott described it plainly: "Yet another example of how quickly attackers move once a vulnerability enters the public domain." WatchTowr recorded hundreds of probing attempts from a small pool of source IP addresses within hours, and as of the most recent reporting from Field Effect, the activity is still mostly scanning and fingerprinting rather than confirmed data theft.
Mostly, for now, is doing a lot of work in that sentence. GeoServer has a documented history of going from "scanning activity observed" to "mass exploited" faster than most self-hosted software. CVE-2024-36401, a different unauthenticated RCE in GeoServer's OGC filter evaluation, was weaponized at internet scale within days for botnet recruitment and cryptomining once someone published a working chain. There is no public exploit for the SQL-to-code-execution path in this bug yet. There is a two-year track record suggesting one is a matter of when, not if, and a live CVE-2023-25158 mitigation that operators still trust and that will not save them here.
It is also worth naming what has not happened yet: as of publication, this bug carries no CVE number and has not appeared on CISA's Known Exploited Vulnerabilities catalog. Unnumbered, unlisted vulnerabilities do not automatically show up in a vulnerability scanner's feed or a compliance dashboard. They get missed precisely because the tooling most SMBs rely on is built to flag CVEs, and this one has not been handed one yet.
Confirm your exposure and hunt for probing
Do this in order: confirm the version, confirm the patch, then check whether anyone already tried the door. If you cannot patch today, pull the instance behind a VPN or IP allowlist, or add a WAF rule blocking requests where a WFS/WMS filter parameter contains SQL metacharacters - it is a blunt control and it will produce false positives, but a blunt control beats an unauthenticated path to your database while you wait on a maintenance window.
# 1) Confirm the running version - reverse proxies often strip this header,
# so check directly against the app server where you can.
curl -s "https://<host>/geoserver/web/" | grep -oE "GeoServer [0-9]+\.[0-9]+(\.[0-9]+)?"
# 2) Confirm you landed on a patched build: 3.0.1, 2.28.5, 2.27.6, or newer.
# Anything older with a PostGIS 12+ datastore is exposed.
# 3) Hunt geoserver.log (and any proxy access log in front of it) for
# jsonArrayContains calls carrying SQL metacharacters - the shape of a
# real injection attempt, not routine filter use.
grep -i "jsonArrayContains" /opt/geoserver*/logs/geoserver.log \
| grep -E "'|--|;|UNION|SELECT|xp_cmdshell"
Three things to check even on an already-patched instance:
What patching alone does not cover
- The exposure window. If your instance was internet-facing between August 12 and whenever you actually patched, treat the probing log entries above as an incident-scoping question, not a formality - run the hunt query before you assume patching closed the book.
- Database account privileges. The datastore credential GeoServer uses to reach PostGIS should have the minimum grants needed to read and write feature data, nothing more. A GeoServer database account with DDL rights or access to unrelated schemas turns any future filter-injection bug from a data-exposure problem into a data-destruction one.
- Everywhere else the library lives.
gt-jdbc-postgisis a GeoTools component, not a GeoServer-exclusive one. Any custom Java application, internal data pipeline, or research tool that pulls GeoTools directly to talk to PostGIS carries the identical unescaped value, whether or not it ever shows up in a GeoServer version banner.
Check the library version, not just the application's
Audit your Maven or Gradle dependency tree for org.geotools:gt-jdbc-postgis specifically, in every internal application that touches PostGIS through GeoTools, and pin it to 33.6, 34.5, 35.1, or newer everywhere it appears - not only in your GeoServer installs. Do that this week. GeoServer's OGC filter code has now produced three critical, unauthenticated vulnerabilities in three years: CVE-2023-25158, CVE-2024-36401, and this one. The pattern lives in how new filter functions get written, not in any single patch, and the next one will not wait for a CVE number before someone starts scanning for it.
Want to know if a GeoServer instance is even on your radar?
We build open-source tools that automate security workflows, including external exposure discovery for the self-hosted services teams forget they are running. Check out scopecheck on GitHub, or book a session to talk through hardening your geospatial or other self-hosted application stack.
