geoserver-3.0.0-安全审计报告

12次阅读
没有评论

GeoServer 3.0.0 源码安全审计报告

审计对象: GeoServer 3.0.0(开发快照,目录 geoserver-3.0.0/src)
审计日期: 2026-08-12
审计方法: 静态代码审计(漏洞模式 grep 搜索 + 关键点人工核验 + 已知 CVE 交叉比对)
代码规模: 约 7,700 个 Java 文件 / 约 121 万行代码
关键依赖版本: GeoTools 35.0、Spring 7.0.8、Spring Security 7.0.5、XStream 1.4.21


1. 执行摘要

本次审计覆盖六大类漏洞方向(反序列化/RCE、命令执行、SQL 注入、未授权访问/越权、硬编码凭据、XSS、SSRF、路径穿越/任意文件)。核心结论:

  • 默认凭据风险突出: 默认管理员 admin/geoserver 在首次启动时自动创建,且 remember-me 签名密钥、keystore 主口令均硬编码为 geoserver。在未改密部署下,攻击者可直接用默认口令登录管理后台。
  • 存在一条完整的高危攻击面: WMS 的 filter/cql_filter 参数属性名未做白名单校验即进入 JDBC 数据存储查询(对应 CVE-2023-25158 模式),而 WFS GetFeature 路径却已修复——同一类漏洞在两个入口修复状态不一致。
  • OGC Filter 表达式求值在 GeoServer 侧无任何函数/accessor 白名单(CVE-2024-36401 模式);实际可利用性取决于捆绑的 GeoTools 35.0 层是否已内置属性访问限制(该版本远新于修复版本,大概率已内置,故定为条件性)。
  • 安全架构整体良好但存在系统性缺口:
    • ✅ XStream 已启用白名单(SecureXStream),CVE-2016-1000027 已封堵
    • ✅ FreeMarker 已启用 MemberAccessPolicy,SSTI(CVE-2024-23642/41877)已封堵
    • ✅ 全站 URL 抓取采用 GeoTools URLCheckers 默认拒绝架构,SSRF 大面被缓解
    • ⚠️ REST 层缺少对象级授权,完全依赖 rest.properties 的 URL 规则兜底(默认 admin-only);一旦按官方注释放宽即产生横向越权
    • ⚠️ 多处 REST 上传/模板接口存在路径穿越与任意文件读写(默认需 admin 认证)
    • ⚠️ GWC 瓦片安全开关 securityEnabled 默认关闭,受保护图层可匿名访问

风险统计

严重性 数量 主要风险
严重 (Critical) 1 默认管理员凭据可被直接登录利用
高危 (High) 9 SQL 注入、未认证 XSS、未授权访问、任意文件读写、REST 越权、SSRF
中危 (Medium) 13 WFS-T filter 注入、SQL 视图参数注入、SLD/OWS SSRF、硬编码默认密钥、存储型 XSS 等
低危 (Low) 14 硬编码缓存库口令、self-XSS、fail-open 模式、死代码 SSRF 等
已修复/已缓解 8 项 XStream、FreeMarker、WFS 主入口、URLChecker、类加载白名单等

2. 审计范围与方法

审计范围: src/ 全部模块——main(核心)、ows、rest、restconfig、wfs-core、wms-core、gwc、security、web、extension/、community/,以及随发行版附带的 data/ 默认配置。

方法:

  1. 漏洞模式静态搜索:对 6 大类共 30+ 组 grep 模式递归搜索,排除 test 代码
  2. 关键命中点人工读取代码段,确认输入可控性、安全边界、认证要求
  3. 已知 GeoServer CVE 交叉比对,确认修复点在当前快照中的存在状态
  4. 对高危发现逐条二次核验(本次报告中的"严重/高危"项均已人工复核源码)

约定:

  • 「未认证」= 匿名请求可达;「需 admin」= 受 REST/Web 默认安全规则保护,需管理员认证
  • 严重性评估同时考虑默认可利用性与实际影响面
  • 所有依赖 GeoTools 35.0 层行为的结论均明确标注为「条件性」

3. 风险总览表

# 漏洞 类别 严重性 认证要求 位置
1 默认管理员凭据 admin/geoserver 自动创建 硬编码凭据 严重 无 GeoServerUser.java:31-40
2 WMS GetMap/GetFeatureInfo filter/cql_filter 属性名未校验 SQL 注入 高 无 GetMap.java:531-535
3 登录页 spring-security-redirect 反射 XSS XSS 高 无 GeoServerBasePage.java:239-253
4 GWC 瓦片安全开关默认关闭 未授权访问 高 无(默认配置) GWCConfig.java:38
5 REST external. 上传任意文件读取 任意文件读 高 admin RESTUtils.java:223-245
6 Importer 多部分/压缩包文件名穿越写 路径穿越 高 admin Directory.java:109-120
7 features-templating 模板名穿越写 路径穿越 高 admin(社区) FileManagerBase.java:81-95
8 CoverageStore 远程 harvest URL 无校验 SSRF 高 admin CoverageStoreFileController.java:110-119
9 OGC Filter 表达式无函数/accessor 白名单 RCE/表达式注入 高(条件) 无 CQLFilterKvpParser.java:24-34
10 REST 控制器普遍缺少对象级授权 越权/IDOR 高(条件) admin→放宽后任意 AbstractCatalogController.java:121-127
11 WFS-T Transaction/Lock filter 未校验 SQL 注入 中 认证 UpdateElementHandler.java:250 等
12 viewparams/env/viewParams SQL 视图参数替换 SQL 注入 中 无 RenderingVariables.java:89-113
13 GetMap SLD= 远程样式(重定向绕过) SSRF 中 无 GetMapKvpRequestReader.java:755,846
14 GetLegendGraphic SLD= 远程样式 SSRF 中 无 GetLegendGraphicKvpReader.java:695-707
15 GetMap REMOTE_OWS_URL 远程 WFS SSRF 中 无 GetMapKvpRequestReader.java:1466-1485
16 WPS wps:Reference 远程输入 SSRF 中 无 RemoteRequestInputProvider.java:100,242
17 wps-download 下载流程 Capabilities URL 回显 SSRF SSRF 高(条件) 无 DownloadMapProcess.java:576-582
18 默认 master/keystore 口令 geoserver 硬编码密钥 中 – GeoServerSecurityManager.java:220
19 remember-me 签名密钥硬编码 geoserver 硬编码密钥 中 – RememberMeServicesConfig.java:21
20 /gwc/rest/web/** 落入弱认证链 未授权访问 中 弱(默认匿名) data/release/security/config.xml:9
21 OpenLayers 预览 LAYERS/STYLES 存储型 XSS XSS 中 admin 查看 OpenLayersPreviewPanel.java:186-187
22 JSONP callback 未转义 XSS 中 无(默认关闭) JSONType.java:183-196
23 UserPasswordController 仅校验 AUTHENTICATED_ROLE 越权 中 认证 UserPasswordController.java:52-57
24 TaskManager zip 解压 zip-slip 路径穿越 中 admin(社区) AppSchemaLocalPublicationTaskTypeImpl.java:106-129
25-38 低危项(见 §4.4) 多类 低 – 见明细

4. 漏洞详情

4.1 严重 — 默认管理员凭据(可直接利用)

[S-1] 默认管理员 admin/geoserver 首次启动自动创建

  • 文件: src/main/src/main/java/org/geoserver/security/impl/GeoServerUser.java:31-40;落地点 src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java:2635-2636、impl/EditableUserDAO.java:96-98
  • 严重性: 严重
  • 描述: 生产代码定义默认管理员账号,且当用户组服务中不存在 admin 时在首次启动自动创建 admin/geoserver。官方虽有"修改默认密码"横幅提醒,但口令本身未随机化。若部署后未立即改密,任何人可用默认口令登录全部管理界面(REST/WEB),直接获得系统完全控制权。随发行版附带的 data/release/security/usergroup/default/users.xml 也内置了该默认哈希。
  • 代码证据:
    public static final String DEFAULT_ADMIN_PASSWD = "geoserver";
    public static final String ADMIN_USERNAME = "admin";
    public static GeoServerUser createDefaultAdmin() {
        GeoServerUser admin = new GeoServerUser(ADMIN_USERNAME);
        admin.setPassword(DEFAULT_ADMIN_PASSWD);
        ...
    }
    
    // GeoServerSecurityManager.java:2635
    if (userGroupService.getUserByUsername(GeoServerUser.ADMIN_USERNAME) == null) {
        userGroupStore.addUser(GeoServerUser.createDefaultAdmin());
    
  • 利用条件: 任意网络可达者,在未改密的默认部署上直接登录。
  • 建议: 首次启动强制改密或生成随机口令;部署文档强制要求修改。

4.2 高危

[H-1] WMS GetMap / GetFeatureInfo filter、cql_filter 属性名未校验 → SQL 注入(CVE-2023-25158 模式)

  • 文件:
    • src/wms-core/src/main/java/org/geoserver/wms/GetMap.java:531-535(filter 直接 setFilter)
    • src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:430,1013(parseFilters 直接透传)
    • src/wms-core/src/main/java/org/geoserver/wms/featureinfo/VectorBasicLayerIdentifier.java:163(GetFeatureInfo 同路径)
    • 解析入口 src/wms-gml/src/main/java/org/geoserver/wms/kvp/FilterKvpParser.java:56-101、src/main/src/main/java/org/geoserver/ows/kvp/CQLFilterKvpParser.java:24-34
  • 严重性: 高
  • 描述: WMS 的 filter(OGC XML)与 cql_filter 参数解析为 GeoTools Filter 后,未做任何属性名(PropertyName)白名单校验即设置到 FeatureLayer 查询,经渲染器 featureSource.getFeatures(query) 下发到 JDBC 数据存储(PostGIS 等)。攻击者通过恶意属性名可在 JDBC 层注入 SQL。关键不对称: WFS GetFeature 路径已有 GetFeature.validateFilter(遍历 PropertyName 校验其存在,§5 已修复项),而整个 WMS 路径(GetMap/GetFeatureInfo)完全没有。
  • 代码证据:
    // GetMap.java:531-535 —— 仅有 validateSort(494/586),无 filter 校验
    Filter filter = SimplifyingFilterVisitor.simplify(Filters.and(ff, layerFilter, dimensionFilter));
    final Query definitionQuery = new Query(source.getSchema().getName().getLocalPart());
    definitionQuery.setFilter(filter);
    definitionQuery.setSortBy(layerSort);
    
  • 利用条件: 图层为 JDBC 数据源(PostGIS 等)时,未认证 GET /wms?request=GetMap&layers=<layer>&filter=<恶意PropertyName> 或 cql_filter=...。实际可利用性与捆绑 GeoTools 35.0 对 SQL 标识符的编码/校验有关。
  • 建议: 在 GetMap/GetFeatureInfo 中复用 GetFeature.validateFilter 属性名校验;对 filter/cql_filter/sortBy 统一走白名单。

[H-2] 登录页 spring-security-redirect 反射 XSS(未认证)

  • 文件: src/web/core/src/main/java/org/geoserver/web/GeoServerBasePage.java:239-253、src/web/core/src/main/java/org/geoserver/web/LoginFormHTMLInclude.java:67-93、模板 src/web/core/src/main/resources/org/geoserver/web/include_login_form.html
  • 严重性: 高
  • 描述: workspace 请求参数被拼接进 redirectUrl,经 FreeMarker 直接写入隐藏域 value="${redirectUrl}",无 HTML 属性转义(已人工核验模板与 importAsString)。攻击者可构造 /web/?workspace="><script>... 在登录页执行任意 JS(钓鱼、会话窃取、凭据采集)。
  • 代码证据:
    // GeoServerBasePage.java:239-241
    String ws = getPageParameters().get("workspace").toOptionalString();
    if (ws != null && !ws.isEmpty()) { redirectUrl = "/web/?workspace=" + ws; }
    // LoginFormHTMLInclude.java:80
    params.put("redirectUrl", redirectUrl != null ? redirectUrl : "");
    template.process(params, writer);   // FreeMarker ${} 默认不转义
    
    <!-- include_login_form.html -->
    <input type="hidden" name="spring-security-redirect" value="${redirectUrl}" />
    
  • 利用条件: 未认证 GET /web/?workspace="><script>alert(document.cookie)</script>。
  • 建议: 对 redirectUrl 做白名单(仅允许站内相对路径)并 HTML 转义;为 FreeMarker 全局开启 ?html 默认转义。

[H-3] GWC 瓦片安全开关默认关闭 → 受保护图层匿名访问

  • 文件: src/gwc/src/main/java/org/geoserver/gwc/config/GWCConfig.java:38、src/gwc/src/main/java/org/geoserver/gwc/dispatch/GeoServerSecurityFilter.java:32-38
  • 严重性: 高
  • 描述: private boolean securityEnabled; 默认 false。GeoServerSecurityFilter.checkSecurity 仅在 isSecurityEnabled() 为真时才执行图层访问校验。即默认部署下,即使 GeoServer 全局安全已开启,GWC 瓦片(含受保护图层)也可匿名访问,绕过图层级权限控制。
  • 代码证据:
    // GWCConfig.java:38 —— 默认 false
    private boolean securityEnabled;
    // GeoServerSecurityFilter.java
    if (GWC.get().getConfig().isSecurityEnabled()) { ... 权限校验 ... }
    
  • 利用条件: 默认配置即生效,匿名请求 GET /gwc/service/wmts?LAYER=<受保护图层>。
  • 建议: 默认开启 securityEnabled,或与全局安全策略联动。

[H-4] REST external. 上传接口任意文件读取

  • 文件: src/rest/src/main/java/org/geoserver/rest/util/RESTUtils.java:223-245
  • 严重性: 高
  • 描述: handleEXTERNALUpload 把请求体直接当路径字符串,new File(stringURL) 若存在即作为资源返回——未调用 URLCheckers.confirm(对比 handleURLUpload 走 IOUtils.upload 有检查)。可读取服务器任意可读文件(/etc/passwd、data/ 内其他凭据等)。
  • 代码证据(已人工核验):
    stringURL = IOUtils.getStringFromStream(inStream);
    inputFile = new File(stringURL);          // 任意路径
    if (!inputFile.exists()) {
        URL fileURL = new URL(stringURL);     // 也是 URL 读取点
        inputFile = IOUtils.URLToFile(fileURL);
    }
    return Files.asResource(inputFile);       // 直接作为资源返回
    
  • 利用条件: 需管理员认证 PUT /rest/workspaces/{ws}/datastores/{store}/external.{format}。默认 REST 规则为 admin-only,故为"高权限下的任意文件读";一旦 REST 规则放宽则风险进一步放大。
  • 建议: 对该路径做路径白名单(限制在数据目录内)并接入 URLCheckers。

[H-5] Importer 多部分/压缩包文件名穿越写

  • 文件: src/extension/importer/core/src/main/java/org/geoserver/importer/Directory.java:109-120,420-434
  • 严重性: 高
  • 描述: child(name) 直接 new File(this.file, name);accept(FileItem) 以客户端提供的 item.getName() 作为目标写文件。文件名含 ../ 可越出上传目录写任意文件(WebShell、覆盖配置等)。压缩包解压路径同样未做 zip-slip 防护。
  • 代码证据:
    public File child(String name) { return new File(this.file, name); }
    public void accept(FileItem item) throws Exception {
        lockDirectory();
        File dest = child(item.getName());   // 未过滤 ../ 
        item.write(dest.toPath());
    
  • 利用条件: 需 admin POST /rest/imports/{id}(multipart 上传)。
  • 建议: 文件名白名单 + 规范化后校验父目录;解压时逐条目校验路径。

[H-6] features-templating 模板名/模式名穿越写(社区模块)

  • 文件: src/community/features-templating/features-templating-core/src/main/java/org/geoserver/featurestemplating/configuration/FileManagerBase.java:81-95;控制器 TemplateRestController.java:103,122,172,192、SchemaRestController.java
  • 严重性: 高
  • 描述: new File(destDir, getName(info) + "." + getExtension(info)),getName(info) 来自 @RequestParam templateName/schemaName,未做 .. 校验,可穿越写出模板/模式文件到任意目录。
  • 代码证据:
    File file = new File(destDir, getName(info) + "." + getExtension(info));
    fos.write(rawContent.getBytes());
    
  • 利用条件: 需 admin,社区模块默认不装。
  • 建议: 文件名净化 + 路径归一化校验。

[H-7] CoverageStore 远程 harvest URL 无校验 → SSRF

  • 文件: src/restconfig/src/main/java/org/geoserver/rest/catalog/CoverageStoreFileController.java:110-119,454-464
  • 严重性: 高
  • 描述: 远程 harvest 的 URL 直接来自请求体字符串,无 URLCheckers.confirm,传给 StructuredGridCoverage2DReader.harvest(ImageMosaic)拉取。可让服务器访问任意内网地址(内网探测、云元数据 169.254.169.254、内网服务)。
  • 代码证据:
    if (method == UploadMethod.remote) { harvestedResources.add(handleRemoteUrl(request)); }
    sr.harvest(null, harvestedResources, GeoTools.getDefaultHints());
    // handleRemoteUrl: return IOUtils.toString(request.getReader());  // 原始 URL
    
  • 利用条件: 需 admin POST /rest/workspaces/{ws}/coveragestores/{store}/remote.tiff(body=http://169.254.169.254/...)。
  • 建议: 该入口接入 URLCheckers 并禁止内网地址。

[H-8] OGC Filter / ECQL 表达式求值无函数/accessor 白名单(CVE-2024-36401 模式,条件性)

  • 文件:
    • 入口 src/main/src/main/java/org/geoserver/ows/kvp/CQLFilterKvpParser.java:24-34、src/main/src/main/java/org/geoserver/util/XCQL.java:28-42、src/wms-gml/src/main/java/org/geoserver/wms/kvp/FilterKvpParser.java:56-101
    • 求值链 src/ows/src/main/java/org/geoserver/ows/util/ClassProperties.java:38-48(把全部零参 get*/is* 方法、含 Object.getClass() 注册为属性 class)、src/ows/src/main/java/org/geoserver/ows/util/OwsUtils.java:127、src/main/src/main/java/org/geoserver/catalog/impl/CatalogPropertyAccessor.java:63-92(SPI 注册)、WPS src/extension/wps/wps-core/src/main/java/org/geoserver/wps/property/BeanPropertyAccessor.java:14-41
  • 严重性: 高(条件性)
  • 描述: 用户可控的 cql_filter/FILTER 经 ECQL.toFilterList/CQL.toFilterList 无任何函数白名单/校验;同时 GeoServer 自注册的 PropertyAccessor 通过 ClassProperties 将 Object.getClass() 暴露为属性 class,OwsUtils.get 反射可达任意 getter。这是 CVE-2024-36401 的典型形态,GeoServer 侧无任何缓解措施。
  • 代码证据:
    // CQLFilterKvpParser.java —— 直通,无过滤
    public Object parse(String value) throws Exception {
        return XCQL.toFilterList(value);   // -> ECQL.toFilterList
    }
    // ClassProperties.java:38-44 —— getClass() 被当作 bean 属性 "class"
    for (Method method : clazz.getMethods()) {
        if ((name.startsWith("get") || name.startsWith("is") || ...) && params.length == 0) {
            getters.put(gp(method), method);
        }
    }
    
  • 利用条件: 未认证 WFS GetFeature&cql_filter=... 或 WMS GetMap&FILTER=...。实际 RCE 可利用性取决于捆绑的 GeoTools 35.0——该版本远新于 CVE-2024-36401 的修复版本(GEOT-7626,GeoTools 30.x),大概率已在 GeoTools 层限制属性访问,故本项为「GeoServer 侧纵深防御缺失」;若部署时降级到旧 GeoTools 则构成未认证 RCE。
  • 建议: 在 CQLFilterKvpParser/FilterKvpParser/ClassProperties 增加函数与 getter 白名单作纵深防御;锁定 GeoTools ≥ 修复版本。

[H-9] REST 控制器普遍缺少对象级授权(IDOR,条件性)

  • 文件: 代表 src/restconfig/src/main/java/org/geoserver/rest/catalog/AbstractCatalogController.java:121-127;所有 REST 控制器无 @Secured/@RolesAllowed/@PreAuthorize
  • 严重性: 高(条件性)
  • 描述: REST 层无任何对象级权限校验,授权完全依赖 data/release/security/rest.properties 的 URL 规则(/**;GET...=ROLE_ADMINISTRATOR)。一旦管理员按官方注释放宽为"登录可读/写",任意认证用户即可读取/修改任意 workspace/store/layer(横向越权),且 checkFullAdminRequired 仅对部分无 workspace 的全局资源生效(如样式删除、图层组删除未调用)。
  • 代码证据:
    // rest.properties 默认
    /**;GET,HEAD,OPTIONS=ROLE_ADMINISTRATOR
    /**;POST,DELETE,PUT=ROLE_ADMINISTRATOR
    
  • 利用条件: 默认 admin-only 缓解;放宽 REST 规则后任意登录用户越权。
  • 建议: 引入对象级授权(每个资源操作校验 workspace/用户所有权);在 REST 控制器层加统一鉴权注解。

4.3 中危

[M-1] WFS-T Transaction(Update/Delete/Replace)与 LockFeature 的 filter 未校验 → SQL 注入

  • 文件: src/wfs-core/src/main/java/org/geoserver/wfs/UpdateElementHandler.java:250、DeleteElementHandler.java:73,114、ReplaceElementHandler.java:105、LockFeature.java:118
  • 严重性: 中
  • 描述: 这些处理器把用户 filter 直接执行(store.getFeatures(filter)/removeFeatures(filter)/getFeatureWriter),未经过 GetFeature 中的 validateFilter 属性名校验。若 feature store 为 JDBC 存储,恶意 PropertyName 可被拼入 SQL。Update 仅校验了被更新的属性名,未校验筛选 filter 的属性名。
  • 建议: 在 WFS-T/Lock 处理器复用 GetFeature.validateFilter。

[M-2] WMS viewparams / env / WFS viewParams → SQL 视图参数直接替换

  • 文件: src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:533-557、src/wms-core/src/main/java/org/geoserver/wms/RenderingVariables.java:89-113、src/wfs-core/src/main/java/org/geoserver/wfs/GetFeature.java:298-300,1353-1354
  • 严重性: 中
  • 描述: 参数值无校验地放入 Hints.VIRTUAL_TABLE_PARAMETERS,GeoTools 对 SQL 视图做直接字符串替换。当图层由带 %param% 占位符的 SQL 视图构成时,攻击者可注入 SQL。已知设计权衡,但需管理员先配置 SQL 视图。
  • 建议: 对 viewparams/env 值做注入字符校验;文档明确 SQL 视图风险。

[M-3] GetMap SLD= 远程样式加载(校验后重定向绕过)

  • 文件: src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:755,846-901
  • 严重性: 中
  • 描述: SLD 参数为用户可控远程样式 URL。L755 有 URLCheckers.confirm,但校验通过后 getHttpInputStream(L867)使用 Apache HttpClient 裸抓取,默认跟随重定向——放行的公网 URL 可 302 跳转内网;响应经解析可用于盲 SSRF/内网探测。默认拒绝策略下被阻断,配置放行规则后成面。
  • 建议: 显式 disableRedirectHandling;放行规则避免宽泛 https?://.*。

[M-4] GetLegendGraphic SLD= 远程样式加载

  • 文件: src/wms-core/src/main/java/org/geoserver/wms/legendgraphic/GetLegendGraphicKvpReader.java:695-707
  • 严重性: 中
  • 描述: 同上模式,L697 显式 URLCheckers.confirm,L699 用 url.openStream()(裸 URLConnection,跟随重定向)抓取。默认拒绝下阻断,放行后成面。
  • 建议: 同 M-3。

[M-5] GetMap REMOTE_OWS_URL 远程 WFS 级联

  • 文件: src/wms-core/src/main/java/org/geoserver/wms/map/GetMapKvpRequestReader.java:1466-1485
  • 严重性: 中
  • 描述: KVP 参数 REMOTE_OWS_URL 用户可控。L1469 有 URLCheckers.confirm,随后拼接 REQUEST=GetCapabilities 走 GeoTools HTTP 层(二次检查)。默认拒绝下阻断;异常被统一掩盖,放行后为盲 SSRF/内网探测。
  • 建议: 同 M-3。

[M-6] WPS wps:Reference 远程输入

  • 文件: src/extension/wps/wps-core/src/main/java/org/geoserver/wps/executor/RemoteRequestInputProvider.java:100,139-181,242,253-332
  • 严重性: 中
  • 描述: wps:Reference 的 href 用户可控,构造函数 L100 与 body 引用 L242 有显式 URLCheckers.confirm,且 buildHttpClient 显式 disableRedirectHandling()(优于其他点);但①系统属性 geoserver.wps.remoteInput.insecureHostname 可关主机名校验;②非 http 协议走 openOtherURL(L321)裸 URLConnection。默认拒绝下阻断。
  • 建议: 移除/默认关闭 insecureHostname 开关;非 http 协议也走受控客户端。

[M-7] wps-download DownloadMapProcess Capabilities URL 回显 SSRF(核验后上调为高)

  • 文件: src/extension/wps-download/src/main/java/org/geoserver/wps/gs/download/DownloadMapProcess.java:103,161,576-582;输入模型 .../download/Layer.java:16-27
  • 严重性: 高(条件性;经二次源码核验确认)
  • 描述: Layer[] layers 为 WPS @DescribeParameter 内联复杂输入,layer.Capabilities(@XmlElement)是用户可控 URL。getServer 用 new WebMapServer(new URL(layer.getCapabilities()), client) 抓取远端 WMS,随后 GetMap 结果图经 RenderedImage 以 RawData 直接回显给调用者。整个 extension/wps-download 源码树无任何显式 URLCheckers.confirm,仅依赖 HTTPClientFinder.createClient()(GeoTools 受控客户端)的隐式检查——是全部 SSRF 点中唯一"无显式校验且结果回显"的反射型 SSRF。默认拒绝策略下被阻断;一旦管理员配置放行规则即可读取内网 WMS 返回图像内容。
  • 代码证据:
    private Supplier<org.geotools.http.HTTPClient> httpClientSupplier = () -> HTTPClientFinder.createClient();
    @DescribeParameter(name = "layer", min = 1, ...) Layer[] layers,
    ...
    server = new WebMapServer(new URL(layer.getCapabilities()), client);
    
  • 调用链: POST /geoserver/wps(gs:Download 内联 layer/Capabilities)→ execute → 远程 GetCapabilities/GetMap → 图像回显。
  • 建议: 增加显式 URLCheckers.confirm;对回显结果做源校验。

[M-8] 默认 master/keystore 口令与 remember-me 签名密钥硬编码

  • 文件: src/main/src/main/java/org/geoserver/security/GeoServerSecurityManager.java:220;src/main/src/main/java/org/geoserver/security/rememberme/RememberMeServicesConfig.java:21 + data/*/security/config.xml(<key>geoserver</key>)
  • 严重性: 中
  • 描述: keystore 主口令(解密数据源口令等敏感配置)默认 geoserver;remember-me cookie 的 HMAC 签名密钥默认 geoserver 且随发行版配置写死。签名密钥公开可预测时,攻击者可伪造合法用户的 remember-me cookie(已知用户名即可)绕过口令登录。
  • 代码证据:
    public static final char[] MASTER_PASSWD_DEFAULT = "geoserver".toCharArray();
    public static final String DEFAULT_KEY = "geoserver";
    
  • 建议: 首次启动随机化;文档强制修改。

[M-9] /gwc/rest/web/** 落入弱认证链

  • 文件: data/release/security/config.xml:9(web 链 path="/web/**,/gwc/rest/web/**,/",含 anonymous + 默认 IS_AUTHENTICATED_ANONYMOUSLY)
  • 严重性: 中
  • 描述: GWC 配置端点 /gwc/rest/web/**(TileLayer 配置读写)被并入 admin UI 链而非 /gwc/rest/**(basic + admin-only)链,在默认 geoserverMetadataSource 下匿名可访问,与 /gwc/rest/** 防护不一致。
  • 建议: 将 /gwc/rest/web/** 移入受保护链。

[M-10] OpenLayers 预览页 LAYERS/STYLES 存储型 XSS

  • 文件: src/web/wms/src/main/java/org/geoserver/wms/web/data/OpenLayersPreviewPanel.java:186-187、模板 ol-load.ftl:9-14
  • 严重性: 中
  • 描述: 图层/样式名 prefixedName() 直接写入 JS 字符串 'LAYERS': '${layer}',未做 JS 上下文转义。图层名含 '、</script> 可逃逸执行。
  • 建议: JS 上下文转义或 JSON 序列化注入。

[M-11] JSONP callback 回调名未转义

  • 文件: src/main/src/main/java/org/geoserver/json/JSONType.java:183-196,277-294(默认关闭,:59-62)
  • 严重性: 中(条件性,需 ENABLE_JSONP=true)
  • 描述: callback 从 FORMAT_OPTIONS 读取,outWriter.write(callback + "(") 直接拼接,可注入任意 JS。
  • 建议: callback 白名单校验(仅 [A-Za-z0-9_$])。

[M-12] UserPasswordController 仅校验 AUTHENTICATED_ROLE

  • 文件: src/restconfig/src/main/java/org/geoserver/rest/security/UserPasswordController.java:52-57
  • 严重性: 中
  • 描述: PUT /rest/security/self/password 仅要求 AUTHENTICATED_ROLE(任何登录用户即可改自己密码)。需确保该端点不落入 REST 链的匿名段,否则存在未认证改密风险。
  • 建议: 校验端点不在匿名链;对改密增加旧密码验证。

[M-13] TaskManager zip 解压 zip-slip(社区)

  • 文件: src/community/taskmanager/core/src/main/java/org/geoserver/taskmanager/tasks/AppSchemaLocalPublicationTaskTypeImpl.java:106-129
  • 严重性: 中
  • 描述: zip 条目名直接拼入 Resources.fromPath(... + entry.getName()) 并写文件,zip-slip 可穿越写出。
  • 建议: 解压逐条目做路径归一化与父目录校验。

4.4 低危

# 漏洞 位置 说明
L-1 HSQLDB 缓存库硬编码口令 geoserver/geopass extension/kml/.../CachedHierarchyRegionatingStrategy.java:172-173 KML 聚合缓存库;作者标注"库内容不敏感"
L-2 H2 内嵌库硬编码 sa/sa community/taskmanager/.../H2DbSourceImpl.java:76-79 社区模块默认超级用户
L-3 GeoFence 认证器硬编码 admin/geoserver 字面量 extension/geofence/.../GeoFenceAuthenticationProvider.java:85 仅日志分支,不授权;但证明默认口令散落生产代码
L-4 GetFeatureInfo sortBy 属性名未校验 wms-core/.../VectorBasicLayerIdentifier.java:146 ORDER BY 拼接面
L-5 GWC CachedLayersPage 关闭转义 web/gwc/.../CachedLayersPage.java:299-307 图层名原样输出(存储型,需目录写入)
L-6 样式编辑器 replaceSelection self-XSS web/wms/.../AbstractStylePage.java:838-841 本地取色器输入
L-7 checkAuthenticationForRole 失败开放 security/.../GeoServerSecurityManager.java:1707-1721 全局安全关闭时管理接口可访问(设计如此,失败开放)
L-8 checkFullAdminRequired 覆盖不全 restconfig/.../AbstractCatalogController.java:121-127 样式删除/图层组删除未强制 admin
L-9 SmartDataLoader 数据源名拼接路径 community/smart-data-loader/.../SmartDataLoaderDataAccessFactory.java:219 datastoreName 未过滤 ..
L-10 OSEO 样例 granule 位置 new File community/oseo/.../CollectionLayerManager.java:199-213 位置来自 DB,弱可控
L-11 GSR 图片资源目录 community/gsr/.../ImageResourceController.java:53-88 仅取 URI 末段,容器归一化基本防穿越
L-12 Importer file:// 协议导入 extension/importer/rest/.../ImportTaskController.java:300-334 仅 file: 协议,本地导入
L-13 DataTransformer.decodeReferenceData 死代码 SSRF extension/wps/.../DataTransformer.java:118-131 无校验 URL 抓取,但当前不可达
L-14 示例/测试凭据 community/rest-openapi/.../GeoServerClient.java:52、geofence-default-override.properties Javadoc/测试资源示例

5. 已修复 / 已缓解项(纵深防御状态)

以下历史高危漏洞在本快照中已被有效封堵,如实记录以免误报:

项 历史漏洞 当前状态 关键位置
XStream 反序列化 RCE CVE-2016-1000027 已修复 SecureXStream 白名单(NoTypePermission.NONE + allowTypes 白名单,可扩展 GEOSERVER_XSTREAM_WHITELIST) config/util/SecureXStream.java:148-191、XStreamPersister.java:296
FreeMarker SSTI CVE-2024-23642 / CVE-2023-41877 已修复 GeoServerMemberAccessPolicy:isConstructorExposed=false + Class/ClassLoader/reflect/security 黑名单 + 静态访问默认关闭 template/GeoServerMemberAccessPolicy.java:61-77
WFS GetFeature 属性名 SQL 注入 CVE-2023-25157 已修复 GetFeature.validateFilter(遍历 PropertyName 校验) + validateSortBy + collectPropertyNames wfs-core/.../GetFeature.java:1491-1514
REST 类加载注入 – 已缓解 AllowedAuthenticationProviderClasses 白名单,resolveConfigClassUncached 先校验再实例化 restconfig/.../AuthenticationProviderRestController.java:488
SSRF 大面 CVE-2024-36404/36405 等 已缓解 GeoTools URLCheckers 默认拒绝(enabled=true 空规则 → 全拒)+ HTTP 客户端/渲染层强制检查 URLChecksConfiguration、GeoTools 35.0
命令执行 – 未发现未认证触发面;外部工具调用(ogr/gdal/wps)均限管理员配置且多走 ProcessBuilder(List) extension/ogr、extension/importer
JNDI 注入 CVE-2022-24847 未发现用户可控 JNDI 名;lookup 均来自管理员数据源配置 taskmanager、security/ldap
Java 原生反序列化 – readObject 命中点均为服务端内部文件/克隆,无 HTTP 直连 AbstractRoleStore.java:312 等

6. 已知 CVE 交叉比对

CVE 描述 本快照状态
CVE-2016-1000027 XStream 反序列化 RCE ✅ 已修复(SecureXStream 白名单)
CVE-2022-24847 WPS JNDI 注入 RCE ✅ 未发现用户可控 JNDI 点
CVE-2023-25157 WFS 属性名 SQL 注入 ⚠️ WFS GetFeature 已修复;WFS-T/Lock 未修复(M-1)
CVE-2023-25158 WMS 属性名 SQL 注入 ❌ 未修复(H-1:GetMap/GetFeatureInfo 无属性名校验)
CVE-2023-35042 WMS 反射 SQL 注入 ⚠️ 同类根因(H-1/M-1/M-2)
CVE-2023-41877 FreeMarker SSTI ✅ 已修复(MemberAccessPolicy)
CVE-2023-51444 日志注入 ⚠️ 未单独验证(非本次重点)
CVE-2024-23642 FreeMarker ?new() SSTI ✅ 已修复(构造函数禁用)
CVE-2024-36401 OGC Filter 表达式 RCE ⚠️ 条件性(H-8:GeoServer 侧无白名单,依赖 GeoTools 35 修复)
CVE-2024-36403 日志注入 ⚠️ 未单独验证
CVE-2024-36404 外部 OWS 数据源 SSRF ⚠️ 部分缓解(H-7/M-3~M-7,默认 URLChecker 拒绝)
CVE-2024-36405 WMTS 远程资源 SSRF ⚠️ 同上

7. 安全架构说明(理解本报告的前提)

  1. REST 层认证默认依赖 URL 规则: data/release/security/rest.properties 默认 /**=ROLE_ADMINISTRATOR,这是大量 REST 侧漏洞(H-4~H-7、H-9)被"admin-only"缓解的根本原因;一旦该文件被放宽(官方注释即演示如何放宽),上述漏洞严重性立即升级。
  2. URL 抓取默认拒绝: URLCheckers(GEOT-6512)默认 enabled=true、空规则 → 一切远程 URL 被拒;管理员需显式配置 urlchecks.xml 放行规则,而规则仅做正则匹配、不解析 DNS/内网 IP,且多处校验后仍跟随重定向。
  3. WFS 与 WMS 修复不对称: 同一类属性名注入漏洞,WFS GetFeature 有 validateFilter 而 WMS 全链路没有——这是本快照最值得警惕的"半修复"状态。
  4. GWC 与主安全体系隔离: GWC 瓦片访问默认不走全局安全策略(securityEnabled=false)。

8. 修复建议(按优先级)

P0 — 立即处置(阻断直接利用)

  1. 修改默认管理员口令(admin/geoserver)、master 口令、remember-me 密钥;部署初始化脚本强制改密或随机生成。
  2. 在 WMS GetMap/GetFeatureInfo/WFS-T/Lock 复用 GetFeature.validateFilter 属性名校验,封堵 SQL 注入(CVE-2023-25158 同源)。
  3. 修复登录页 spring-security-redirect 反射 XSS(白名单 + HTML 转义)。

P1 — 高优先级
4. 将 GWC securityEnabled 默认设为 true 并与全局安全联动。
5. RESTUtils.handleEXTERNALUpload 与 CoverageStoreFileController 远程 harvest 接入 URLCheckers 与路径白名单。
6. Importer/features-templating/TaskManager 的文件名与 zip 条目做路径归一化校验(防穿越写)。
7. 为 CQLFilterKvpParser/FilterKvpParser/ClassProperties 增加函数与 getter 白名单(纵深防御,锁定 GeoTools ≥ 修复版本)。
8. REST 层引入对象级授权;checkFullAdminRequired 覆盖所有全局资源删除操作。

P2 — 中期加固
9. SLD/REMOTE_OWS_URL/WPS 远程输入的 URL 抓取显式禁止重定向;DownloadMapProcess 增加显式 URLCheckers.confirm。
10. /gwc/rest/web/** 移入受保护认证链。
11. OpenLayers 预览/JSONP/样式编辑器输出转义;sortBy 纳入属性名校验。
12. 移除 geoserver.wps.remoteInput.insecureHostname 逃生通道。

P3 — 常规整改
13. 低危硬编码口令(HSQLDB/H2)改为随机生成或配置化;移除 GeoFence 认证器中默认口令字面量。
14. DataTransformer.decodeReferenceData 死代码删除或补校验;SmartDataLoader/OSEO/GSR 路径拼接净化。
15. 文档明确 SQL 视图(viewparams)、REST 规则放宽的固有风险。


9. 附录:关键文件索引

默认凭据      src/main/.../security/impl/GeoServerUser.java
WMS SQL 注入  src/wms-core/.../wms/GetMap.java
             src/wms-core/.../wms/map/GetMapKvpRequestReader.java
             src/wms-core/.../wms/featureinfo/VectorBasicLayerIdentifier.java
登录页 XSS    src/web/core/.../web/GeoServerBasePage.java
             src/web/core/.../web/LoginFormHTMLInclude.java
             src/web/core/src/main/resources/org/geoserver/web/include_login_form.html
GWC 未授权    src/gwc/.../gwc/config/GWCConfig.java
任意文件读    src/rest/.../rest/util/RESTUtils.java
路径穿越      src/extension/importer/core/.../importer/Directory.java
             src/community/features-templating/.../configuration/FileManagerBase.java
SSRF         src/restconfig/.../rest/catalog/CoverageStoreFileController.java
             src/wms-core/.../wms/map/GetMapKvpRequestReader.java
             src/wms-core/.../wms/legendgraphic/GetLegendGraphicKvpReader.java
             src/extension/wps/wps-core/.../wps/executor/RemoteRequestInputProvider.java
             src/extension/wps-download/.../wps/gs/download/DownloadMapProcess.java
表达式注入    src/main/.../ows/kvp/CQLFilterKvpParser.java
             src/ows/.../ows/util/ClassProperties.java
             src/main/.../catalog/impl/CatalogPropertyAccessor.java
REST 越权    src/restconfig/.../rest/catalog/AbstractCatalogController.java
已修复示例    src/main/.../config/util/SecureXStream.java
             src/main/.../template/GeoServerMemberAccessPolicy.java
             src/wfs-core/.../wfs/GetFeature.java(validateFilter)

报告完毕。本报告基于静态源码审计,未进行动态验证;标注为「条件性」的项受捆绑的 GeoTools 35.0 实际行为影响,建议在真实部署环境复测确认。

正文完
 0
评论(没有评论)