GeoServer SQL 注入漏洞分析(CVE-2023-25157)
GeoServer SQL 注入漏洞分析(CVE-2023-25157)
0x01 GeoServer & GeoTools
GeoServer 是一个用 Java 编写的开源软件服务器,允许用户共享和编辑地理空间数据,GeoServer 基于 Spring 开发,使用到了 GeoTools 库。
GeoTools 是一个开源的 Java 库,提供对地理数据空间工具,GeoServer 许多核心功能使用 GeoTools 实现,如:数据读写转换。
0x02 漏洞介绍
GeoServer 和 GeoTools 发布了 CVE-2023-25157 和 CVE-2023-25158,OGC 查询存在 SQL 注入漏洞。GeoServer 支持 OGC 过滤器表达式语言和 OGC 通用查询语言 (CQL),主要影响 Web 要素服务 (WFS) 、Web 地图服务 (WMS) 和 用于 ImageMosaic 覆盖的 Web 覆盖服务 (WCS) 协议,已知:
- PropertyIsLike 与带有字符串字段的任何数据库一起使用时,或者与启用了编码功能的 PostGIS 数据存储一起使用时
- strEndsWith 启用了编码功能的 PostGIS DataStore 一起使用时
- strStartsWith 启用了编码功能的 PostGIS DataStore 一起使用时
- FeatureId 与具有字符串主键列的任何数据库表一起使用并禁用预编译时
- jsonArrayContains 字符串或 JSON 字段以及 PostGIS 或 Oracle DataStore 一起使用时(仅 GeoServer 2.22.0 以上版本受影响)
- DWithin 与 Oracle DataStore 一起使用时
对于 GeoTools 在使用 JDBCDataStore 实现执行 OGC 过滤器时存在 SQL 注入漏洞:
- PropertyIsLike 启用“编码功能”的 PostGIS DataStore 或者任何带有字符串字段的 JDBCDataStore
- strEndsWith 启用“编码功能”的 PostGIS DataStore
- strStartsWith 启用“编码功能”的 PostGIS DataStore
- FeatureId JDBCDataStore禁用预编译并且有字符串主键(Oracle 不受影响,SQL Server 和 MySQL 没有启用预准备语句的设置,PostGIS 则受影响)
- jsonArrayContains 带有字符串或 JSON 字段的 PostGIS 和 Oracle DataStore
- DWithin 仅在 Oracle DataStore 中
0x03 影响版本
GeoServer <2.21.4,<2.22.2
GeoTools <28.2、<27.4、<26.7、<25.7、<24.7
官方已发布补丁,请及时更新。
0x04 环境搭建
在这里使用 GeoServer 2.21.3,下载完成后解压:
1 | unzip geoserver-2.21.3-bin.zip |
进入到 geoserver-2.21.3-bin/bin
目录下,执行启动程序
1 | sh startup.sh |
启动成功后,访问 http[:]//x.x.x.x:8080/geoserver/web/ 即可。
使用 Docker 搭建 PostgreSQL
1 | docker run -e POSTGRES_PASSWORD=password -d -p 5433:5432 postgres:latest |
进入容器,安装 postgis
拓展
1 | apt search postgis |
postgresql-14-postgis-3-scripts
要根据你 PostgreSQL 来安装,本次使用到的 PostgreSQL 为 PostgreSQL 14.1
此时数据可参考官方文档:https://docs.geoserver.org/latest/en/user/gettingstarted/postgis-quickstart/index.html
编辑 startup.sh
启动脚本添加远程调试参数:
1 | exec "${_RUNJAVA}" ${JAVA_OPTS:--DNoJavaOpts -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005} "${MARLIN_ENABLER:--DMarlinDisabled}" "${RENDERER:--DDefaultrenderer}" "-Djetty.base=${GEOSERVER_HOME}" "-DGEOSERVER_DATA_DIR=${GEOSERVER_DATA_DIR}" -Djava.awt.headless=true -DSTOP.PORT=8079 -DSTOP.KEY=geoserver -jar "${GEOSERVER_HOME}/start.jar" |
至此环境搭建结束。
0x05 漏洞分析
由于存在多个注入点,在这里以 strStartsWith
作为案例进行分析,将项目导入 IDEA,开启 DEBUG 调试,下断点定位到 ogr.geotools.jdbc
下的 getReaderInternal
函数
在查询之前 this.getDataStore().getConnection(this.getState());
会执行 select now()
判断是否能正常连接数据库。
继续跟进到 selectSQL
中
在 selectSQL
函数中 selectColumns
会对数据库中的字段进行遍历,并拼接出 SQL 语句
拼接相关函数如下:
拼接完成后 SQL 语句如下:
1 | SELECT "gid","bin",encode(ST_AsEWKB("the_geom"), 'base64') as "the_geom" FROM "public"."nyc_buildings" WHERE |
接下来是对 filter
的处理
在 filter
中将我们输入的 CQL_FILTER
转换成 SQL 后语句后拼接到 WHERE
后面
因此最后拼接出来的 SQL 语句如下:
1 | SELECT "gid","bin",encode(ST_AsEWKB("the_geom"), 'base64') as "the_geom" FROM "public"."nyc_buildings" WHERE ("bin"::text LIKE 'x') = true and 1=(SELECT CAST ((SELECT version()) AS INTEGER)) -- %') = true |
在 JDBCFeatureReader
中由 executeQuery
执行 SQL 语句
总结一下:org.geotools.jdbc
下的 getReaderInternal()
函数对用户输入的查询进出处理,进一步调用 selectSQL
生成对应数据库的 SQL 查询语句,生成数据库的查询语句后,会对判断是否存在 CQL_FILTER
查询条件,如果是存在则开始处理用户输入的 CQL_FILTER
条件,由 encodeToString(Filter filter)
将 CQL_FILTER
转换为 SQL 语句,再由 FilterToSQL filter
拼接到 WHETE
后面,最后 JDBCFeatureReader
的 this.runQuery
执行带有注入的 SQL 语句,完成注入。
最终的整个漏洞的调用栈如下:
1 | <init>:153, JDBCFeatureReader (org.geotools.jdbc) |
0x06 修复
目前 GeoServer 和 Geotools 官方均已发布修复版本,查看 GeoServer 官方提交的补丁,在 src/community/jdbcconfig/src/main/java/org/geoserver/jdbcconfig/internal/ConfigDatabase.java
中添加了模块org.geoserver.jdbcloader.JDBCLoaderProperties
模块用于配置文件 jdbcconfig/jdbcconfig.properties
中的 JDBCConfig 模块属性字段并更改了构造函数以包含此属性字段。
还修改了 src/community/jdbcconfig/src/main/java/org/geoserver/jdbcconfig/internal/OracleDialect.java
中的插入语法
而在 GeoTools 提交的补丁中,修改 modules/library/jdbc/src/main/java/org/geotools/data/jdbc/FilterToSQL.java
添加了EscapeSql
模块和 escapeBackslash
字段对 SQL 注入进行防御
0x07Refer
https://github.com/murataydemir/CVE-2023-25157-and-CVE-2023-25158
https://docs.geoserver.org/latest/en/user/introduction/overview.html