本仓库包含两个用于调查 GeoServer WFS 端点的 Python 脚本。
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
通过初步筛选并不代表确认存在漏洞。只有当第二次更长时间的测量也通过时,才会输出参数块。
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 元素,并选择名称类似 ID 或数字的字符串/JSON 字段。
根据调用方式检查候选对象:
--sleep:样本值必须是语法有效的 JSON。--sleep N:测量一个控制请求和一个基于时间的检测请求。此时跳过 JSON 值检查。结果立即输出并刷新。
之后才从正在进行的 GetCapabilities 响应中读取下一个 typeName。
这样,无需在处理完整个大型 GetCapabilities 响应之前就能显示第一个结果。
无 --sleep 时,自动模式默认考虑名称类似 ID 或数字的字符串/JSON 字段。
使用 --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% 的请求 Sleep 时间,则候选对象被视为阳性。--timing-result-type auto 首先检查 resultType=hits,如果结果为阴性,则随后检查 resultType=results。
初步阳性的测量结果必须使用更长的 Sleep 时间进行确认。如果没有显式的 --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
限制到某个命名空间:
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 | – | 将自动模式限制为某个要素类型;fields 和 values 模式必填 |
--namespace PREFIX | – | 仅考虑此命名空间前缀的图层 |
--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 | 所需 Sleep 时间的比例;范围 0.5 到 1.0 |
--confirm-sleep SECONDS | 0/自动 | 确认 Sleep;0 使用至少 3 倍于首次 Sleep 时间,范围 1 到 10 |
--start-layer-index N |
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 | PoC 模式的要素类型 |
--field_name NAME | PoC 模式必填 | 用于 jsonArrayContains 的简单 XSD 字段名 |
--field-name NAME | 别名 | --field_name 的别名 |
--valid-fields N | – | 启用清单模式并在 N 个有效图层后停止 |
--valid-diagnose FILE | – | 将详细清单报告写入 FILE;需要 --valid-fields |
--sleep SECONDS | PoC:5,清单:关闭 | 相应模式的 Sleep 时长 |
--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 时,候选对象必须根据 --candidate-scope 通过 XSD/名称检查和 JSON 语法检查。--sleep 时,仅输出时间上确认的候选对象作为参数块;阴性测量结果显示为 stderr 上的 [sleep-check]。--max-layers。Cannot do natural order without a primary key对于第一个 GetFeature 页面,脚本不发送 startIndex=0,因为某些没有主键的 GeoServer/JDBC 图层会因此强制自然排序。在导出更多页面时,此类图层可能仍需要主键或服务器端支持的排序。
schema does not define ...Type脚本同时考虑常见的 <LayerName>Type 以及全局 XSD 图层元素中引用的不同或匿名的 complexType。如果错误仍然存在,应使用 --diagnose 检查相关的 DescribeFeatureType 响应。
完整的 OWS 错误消息会输出到 stderr。常见原因包括不支持的属性、服务器端分页设置或特定于图层的数据源配置。
python3 wfs_inventory.py --help
python3 geoserver_sqli_working.py --help
0| 跳过前 N 个流式图层 |
--max-layers N | 0 | 最多处理 N 个图层;0 表示无限制 |
--valid-fields N | 0 | 在 N 个有效图层块后停止;0 表示无限制 |
--valid N | 别名 | --valid-fields 的向后兼容别名 |
--max-features N | 0 | 在 values 模式下 N 个要素后停止;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 |