GeoServerjsonArrayContains未授权RCE:一个jsonb_path_exists引入的SQL注入0day

admin 2026-08-21 07:01:33 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: GeoServer存在一个由GeoTools引入的jsonArrayContains未授权SQL注入0day漏洞(CVSS9.8),攻击者可通过WFSGetFeature的CQL_FILTER参数注入恶意SQL,在数据库高权限下利用PostgreSQL的COPY…TOPROGRAM实现远程代码执行。国内暴露资产约5320个,官方已合并修复PR但未发布版本。建议立即限制WFS暴露、使用最小权限数据库用户并部署WAF拦截相关关键字。 综合评分: 90 文章分类: 漏洞分析,WEB安全,红队,渗透测试,应急响应


cover_image

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&nbsp;(SELECT&nbsp;1)&nbsp;TO&nbsp;PROGRAM&nbsp;'<command>'

命令以数据库OS用户身份在/bin/sh中执行,可以反弹shell、写webshell、横向移动。

注入需要穿越三层结构:JSONPath双引号 → SQL单引号 → jsonb_path_exists外层表达式。闭合后用分号开始新语句,用SQL注释吞掉尾部。注意WFS和WMS查询形状不同,payload不能互换。


利用条件

不是随便一个GeoServer就能RCE,需要同时满足:

  1. WFS服务未要求认证
  2. 后端使用PostgreSQL/PostGIS数据存储
  3. 存在能被当作JSON解析的属性
  4. PostgreSQL >= 12(12以下不走新分支)
  5. 实现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 \
&nbsp; --workspace jac_sqli --layer poi_info --column mp \
&nbsp; --cmd&nbsp;"id > /tmp/marker.txt"

# 实际执行(仅限授权环境)
python3 exploit.py --url http://127.0.0.1:8085/geoserver \
&nbsp; --auto --cmd&nbsp;"id > /tmp/marker.txt"&nbsp;--execute

利用是盲的——HTTP响应不返回命令输出。Multiple ResultSets只代表堆叠语句信号,不能证明命令执行成功,必须在数据库端核对marker文件和statement log。


修复建议

官方已合并修复PR(GeoTools #5829,GEOT-7958),对expected参数调用escapeJsonLiteral()转义。但截至发稿修复版本尚未发布,临时缓解:

  1. 限制或关闭WFS对外暴露,或强制认证
  2. 数据库连接用最小权限用户,撤销superuser和pg_execute_server_program
  3. WAF拦截CQL_FILTER中的jsonArrayContains、COPY、PROGRAM、pg_sleep、pg_read_file等关键字
  4. 数据库置独立非特权环境,不挂载宿主敏感目录

根治必须消除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》

评论:0   参与:  0