文章总结: GeoServer存在一个由GeoTools引入的jsonArrayContains未授权SQL注入0day漏洞(CVSS9.8),攻击者可通过WFSGetFeature的CQL_FILTER参数注入恶意SQL,在数据库高权限下利用PostgreSQL的COPY…TOPROGRAM实现远程代码执行。国内暴露资产约5320个,官方已合并修复PR但未发布版本。建议立即限制WFS暴露、使用最小权限数据库用户并部署WAF拦截相关关键字。 综合评分: 90 文章分类: 漏洞分析,WEB安全,红队,渗透测试,应急响应
GeoServer jsonArrayContains 未授权RCE:一个jsonb_path_exists引入的SQL注入0day
零日手记 零日手记
随笔漫记安全路
2026年8月16日 09:35 北京
在小说阅读器读本章
去阅读
8月13日,安全研究员秋风在X上公开了一个GeoServer的0day——通过jsonArrayContains函数未授权SQL注入,数据库高权限下直接RCE。CVSS 9.8,国内暴露资产5320个。官方已合并修复PR但尚未发布修复版本。
GeoServer是什么
GeoServer是OSGeo维护的开源地理空间数据服务器,实现了WMS、WFS、WCS等OGC标准服务。说白了就是个地图服务器,政府、企业、科研机构用得很多。底层依赖GeoTools做JDBC数据访问和CQL/ECQL过滤器到SQL的转换。漏洞就出在这个转换环节。
CVSS 3.1评分9.8。PoC、EXP、技术细节均已公开。暂无CVE编号。
漏洞原理
GeoTools的GEOT-7589给PostgreSQL 12+引入了jsonb_path_exists优化。引入前,jsonArrayContains走旧版jsonb @>编码逻辑,对应值会做转义。引入后,第三参数expected直接字符串拼接进SQL,没有转义、没有参数化。
代码层面的sink长这样:
Object value = ((LiteralExpressionImpl) expected).getValue();
return "(@.%s == \"%s\")".formatted(jsonPath[lastIndex], value);
这个value没有经过JSONPath转义,没有经过SQL literal encoder,也没有进入JDBC bind参数,被同时嵌套在JSONPath双引号、SQL单引号和jsonb_path_exists()调用中。
正常输入生成:
jsonb_path_exists("json_column"::jsonb, '$ ? (@.key == "expected")')
攻击者控制expected,就能依次退出JSONPath字符串、SQL字符串和外层SQL表达式,改变后续SQL语法结构。标准SQL注入。
攻击链路
匿名GetFeature + CQL_FILTER
→ ECQL parser
→ JsonArrayContainsFunction(column, path, expected)
→ JDBCFeatureSource.getCountInternal()
→ FilterToSqlHelper.encodeJsonArrayContains()
→ constructEquality() [原始拼接sink]
→ Statement.executeQuery(countSql)
→ PostgreSQL
通过WFS GetFeature的CQL_FILTER参数注入恶意SQL,先能做数据泄露和篡改。数据库连接用户是超级用户或持有pg_execute_server_program权限时,通过PostgreSQL的COPY…TO PROGRAM执行系统命令:
COPY (SELECT 1) TO PROGRAM '<command>'
命令以数据库OS用户身份在/bin/sh中执行,可以反弹shell、写webshell、横向移动。
注入需要穿越三层结构:JSONPath双引号 → SQL单引号 → jsonb_path_exists外层表达式。闭合后用分号开始新语句,用SQL注释吞掉尾部。注意WFS和WMS查询形状不同,payload不能互换。
利用条件
不是随便一个GeoServer就能RCE,需要同时满足:
- WFS服务未要求认证
- 后端使用PostgreSQL/PostGIS数据存储
- 存在能被当作JSON解析的属性
- PostgreSQL >= 12(12以下不走新分支)
- 实现RCE需数据库角色是superuser或有pg_execute_server_program权限
没有PostgreSQL/PostGIS数据存储时根本不进入这个sink。普通权限角色只能SQL注入读写数据,不能执行命令。
容易被忽略的一点:GeoTools的preparedStatements=true和pgJDBC的preferQueryMode=extended都不能作为缓解措施。pgJDBC 42.7.11默认就是extended,但驱动仍会拆分复合查询依次执行。preparedStatements复用同一个FilterToSqlHelper,raw concat sink依然存在。
影响范围
受影响版本:
- GeoServer 2.24.5 ~ 3.0.0(GeoTools 30.5 ~ 35.0),由GEOT-7589(2024年6月引入)回归
- 已确认:GeoServer 2.26.0(GeoTools 32.0)
- GeoTools 30.4/31.2及以下不含新分支,不受影响
资产暴露情况:
- 国内5320个,关联IP 353个
- 全球34038个,关联IP 1515个
PoC状态
GitHub已有完整研究仓库(mhtsec/GeoServer-jsonArrayContains-PG-RCE),含exploit.py、隔离复现环境、原理文档。还有社区补丁(ubitquity/UBITQUITY-GeoServer-Security-Patch)通过escapeLiteral()做转义缓解。
PoC使用:
# 基线检查
python3 exploit.py --url http://127.0.0.1:8085/geoserver --auto --check
# 离线预览(不发请求)
python3 exploit.py --url http://127.0.0.1:8085/geoserver \
--workspace jac_sqli --layer poi_info --column mp \
--cmd "id > /tmp/marker.txt"
# 实际执行(仅限授权环境)
python3 exploit.py --url http://127.0.0.1:8085/geoserver \
--auto --cmd "id > /tmp/marker.txt" --execute
利用是盲的——HTTP响应不返回命令输出。Multiple ResultSets只代表堆叠语句信号,不能证明命令执行成功,必须在数据库端核对marker文件和statement log。
修复建议
官方已合并修复PR(GeoTools #5829,GEOT-7958),对expected参数调用escapeJsonLiteral()转义。但截至发稿修复版本尚未发布,临时缓解:
- 限制或关闭WFS对外暴露,或强制认证
- 数据库连接用最小权限用户,撤销superuser和pg_execute_server_program
- WAF拦截CQL_FILTER中的jsonArrayContains、COPY、PROGRAM、pg_sleep、pg_read_file等关键字
- 数据库置独立非特权环境,不挂载宿主敏感目录
根治必须消除expected参数的原始拼接,用jsonb_path_exists变量对象承载比较值,通过JDBC bind传入。黑名单和WAF关键字过滤不够可靠。
参考资料
[1] 原始披露 – https://x.com/q1uf3ng/status/2087490992723407096[1]
[2] PoC仓库 – https://github.com/mhtsec/GeoServer-jsonArrayContains-PG-RCE[2]
[3] 社区补丁 – https://github.com/ubitquity/UBITQUITY-GeoServer-Security-Patch[3]
[4] GeoTools修复PR #5829(GEOT-7958)- https://github.com/geotools/geotools/pull/5829[4]
[5] GeoTools GEOT-7589(漏洞引入)- https://github.com/geotools/geotools/pull/4805[5]
引用链接
[1]https://x.com/q1uf3ng/status/2087490992723407096
[2]https://github.com/mhtsec/GeoServer-jsonArrayContains-PG-RCE
[3]https://github.com/ubitquity/UBITQUITY-GeoServer-Security-Patch
[4]https://github.com/geotools/geotools/pull/5829
[5]https://github.com/geotools/geotools/pull/4805
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:随笔漫记安全路 零日手记 零日手记《GeoServer jsonArrayContains 未授权RCE:一个jsonbpathexists引入的SQL注入0day》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论