
Scripts de Python para inventariar endpoints WFS de GeoServer y verificar vulnerabilidades de inyección SQL basadas en tiempo en PostGIS/GeoTools, con un modo PoC dedicado para pruebas autorizadas.
Este repositorio contiene dos scripts de Python para la investigación de endpoints WFS de GeoServer.
wfs_inventory.pyPropósito: Inventariar capas, campos XSD y valores WFS, así como verificar candidatos opcionalmente mediante una comprobación basada en tiempo.
Ejemplos de ejecución:
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.pyPropósito: Punto de entrada combinado para el inventario, así como un modo PoC separado.
Ejemplos de ejecución:
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()"
Nota importante: La comprobación basada en tiempo y el modo PoC solo deben utilizarse contra sistemas para los que exista una autorización expresa de prueba. El modo de inventario normal utiliza exclusivamente operaciones WFS regulares.
Opcionalmente, hacerlos ejecutables:
chmod +x wfs_inventory.py geoserver_sqli_working.py
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4
La ruta estándar /geoserver/wfs se añade automáticamente. Por tanto, las
siguientes indicaciones son equivalentes:
https://HOST
https://HOST/geoserver
https://HOST/geoserver/wfs
En caso de una instalación diferente, debe especificarse la ruta WFS completa.
Solo para sistemas expresamente autorizados:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1
Durante la comprobación aparece una medición por candidato en 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 screen superado no es aún un hallazgo positivo. Solo si la segunda medición, más larga, también se supera, se emite un bloque de parámetros.
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
--valid-diagnose FILE activa automáticamente el diagnóstico detallado y lo
escribe en el archivo especificado.
El modo estándar auto procesa la información WFS en este orden:
GetCapabilities se abre una vez:
/geoserver/wfs?service=WFS&acceptVersions=2.0.0&request=GetCapabilities
La respuesta XML se transmite en streaming. En cuanto se encuentra un
FeatureType/Name, queda fijado el siguiente typeName.
Para esta capa se ejecuta inmediatamente DescribeFeatureType con
version=2.0.0.
El script extrae los elementos XSD y selecciona campos String/JSON con nombres similares a ID o numéricos.
El candidato se comprueba según la invocación:
--sleep: los valores de muestra deben ser JSON sintácticamente válido.--sleep N: se miden una consulta de control y una consulta de
comprobación basada en tiempo. La verificación del valor JSON se omite.El resultado se emite inmediatamente y se vacía el buffer.
Solo después se lee el siguiente typeName de la respuesta
GetCapabilities en curso.
De este modo, no es necesario procesar completamente una respuesta
GetCapabilities grande antes de que aparezca el primer resultado.
Sin --sleep, el modo automático considera por defecto campos String/JSON con
nombres similares a ID o numéricos.
Con --sleep, --candidate-scope auto utiliza en cambio todos los campos
simples que no sean de geometría. El tipo XSD y el patrón de nombre de ID ya no
bloquean la comprobación de tiempo. Esto evita falsos negativos en tipos XSD
numéricos, de fecha/booleano o específicos del fabricante.
Los patrones de nombre reconocidos incluyen, entre otros:
id
*_id
*_fid
nr_*
*_nr
*nummer*
fid
uuid
guid
key
objectid
En la comprobación basada en tiempo, el nombre del campo debe ser además un
identificador simple con el formato [A-Za-z_][A-Za-z0-9_]*.
Sin --sleep, se comprueba si los valores observados pueden interpretarse
sintácticamente como JSON. Por eso, por ejemplo, la cadena "383205" también
se considera candidata, porque su contenido representa un número JSON válido.
Esta comprobación es una heurística y no una prueba de vulnerabilidad.
Con --sleep 1, solo decide la medición de tiempo. Un candidato se considera
positivo por defecto si la consulta de comprobación requiere al menos un 70 por
ciento adicional del tiempo de sleep solicitado en comparación con la consulta
de control. --timing-result-type auto comprueba primero resultType=hits y,
en caso de resultado negativo, posteriormente resultType=results.
Las mediciones provisionalmente positivas se confirman obligatoriamente con un
tiempo de sleep más largo. Sin un --confirm-sleep explícito, el script usa
max(3, --sleep * 3), limitado a 10 segundos. Así, un único pico de latencia
con --sleep 1 ya no conduce a vulnerable=true.
La salida estándar contiene un bloque por capa válida:
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 contiene el endpoint WFS normalizado y los valores
adecuados para --typename y --field_name.
--valid-fields N cuenta bloques de capas, no campos individuales. Si existen
menos de N capas válidas, se sigue investigando el catálogo restante.
Con --max-layers se puede limitar el tiempo máximo de ejecución.
Las opciones --diagnose o --valid-diagnose FILE añaden, entre otros:
GetCapabilities utilizada,DescribeFeatureType,GetFeature generada yEjemplo:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 10 \
--valid-diagnose diagnose.txt
python3 wfs_inventory.py \
--url https://HOST \
--mode layers
Limitar a un namespace:
python3 wfs_inventory.py \
--url https://HOST \
--mode layers \
--namespace fink
python3 wfs_inventory.py \
--url https://HOST \
--mode fields \
--layer namespace:layer
Cada campo se emite como objeto JSON con nombre, tipo XSD, nillable,
id_candidate y usable_property.