GHSA-cgfv-jrfp-2r7v: Openremote manager SQL Injection Vulnerability

HIGH
CWE-89
Published
Advisory Summary

GHSA-cgfv-jrfp-2r7v is a HIGH sql injection vulnerability (CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')) affecting Openremote manager libraries in the Maven ecosystem. The issue involves openremote has authenticated sql injection via datapoint crosstab export. Remediation is available by upgrading affected packages to version 1.26.0.

GHSA-cgfv-jrfp-2r7v at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-cgfv-jrfp-2r7v
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Affected Product Openremote manager
Package Ecosystem Maven
Published Date
Last Updated

How to Fix GHSA-cgfv-jrfp-2r7v

To remediate this vulnerability, upgrade all affected Openremote manager dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

io.openremote:openremote-manager fixed: 1.26.0
<dependency>
    <groupId>io.openremote</groupId>
    <artifactId>openremote-manager</artifactId>
    <version>1.26.0</version>
</dependency>
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

mvn dependency:tree -Dincludes=io.openremote

How to Check if You Are Affected

To determine whether your Java application is affected by GHSA-cgfv-jrfp-2r7v, inspect your build dependencies using the Maven Dependency Plugin:

mvn dependency:tree -Dincludes=io.openremote

Analyze the terminal output to locate any version of Openremote manager in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.26.0), your application is affected.

Note: The mvn dependency:tree -Dincludes=io.openremote command inspects the resolved dependency graph declared in your project's POM files; it does not perform automated security scanning or inspect runtime dynamic classpaths. Always verify runtime container classpaths and shaded JARs.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'), where the product constructs all or part of an sql command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended sql command when it is sent to a downstream component. without sufficient removal or quoting of sql syntax in user-controllable inputs, the generated sql query can cause those inputs to be interpreted as sql instead of ordinary user data. In Openremote manager, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The datapoint export API builds a PostgreSQL crosstab export query by concatenating asset display names into raw SQL. An authenticated user who can create or rename an asset and then request a crosstab datapoint export can inject SQL through the asset name. The injected query output is streamed back to the caller inside the normal ZIP/CSV export response. This creates a practical database exfiltration primitive through the application API. In a multi-tenant deployment, this can expose data outside the attacker's tenant if the application database role can read shared manager tables. ## Affected Component - Datapoint export endpoint for asset datapoints. - Crosstab export formats, specifically CSV crosstab-style exports. - Query builder path that constructs a `COPY (SELECT ... FROM crosstab(...)) TO STDOUT` statement. ## Security Impact Impact is high. A remote authenticated attacker with asset read/write capabilities can: - Store SQL syntax inside an asset name. - Trigger the crosstab export path for that asset. - Cause the backend to execute attacker-influenced SQL through the PostgreSQL connection used by the manager service. - Receive injected `SELECT` results in the exported CSV contained in the ZIP response. The demonstrated impact is database data exfiltration. The proof of concept safely retrieved database execution context and an aggregate table count. A real attacker could adapt the injected `SELECT` to read other database tables accessible to the application database role. This is especially sensitive in multi-tenant deployments because application tables commonly contain data for multiple realms/tenants in the same database. ## Attack Preconditions The attacker needs: - A valid authenticated session. - Permission to create or rename at least one asset. - Permission to read/export datapoints for at least one attribute on that asset. - Access to a crosstab datapoint export format. No direct database access is required. No server filesystem access is required. No token forgery is required. ## Technical Details The export implementation derives a crosstab header from the asset name and attribute name. It then embeds that header into two SQL contexts: 1. A PostgreSQL double-quoted column identifier: ```sql "<asset name> : <attribute name>" text ``` 2. A category query passed to `crosstab(...)`, wrapped in a fixed dollar-quoted delimiter: ```sql $cat$ SELECT header FROM (VALUES ('<asset name> : <attribute name>')) AS t(header) $cat$ ``` The current escaping is incomplete: - Single quotes are escaped in one string-literal context. - Double quotes in asset names are not escaped before being placed inside quoted identifiers. - The fixed dollar-quote delimiter is not protected against an asset name containing the delimiter token. As a result, an attacker-controlled asset name can break out of the intended SQL grammar boundary and append SQL to the generated `COPY ... TO STDOUT` query. Because the backend streams `COPY` output into the export response, injected query rows are returned to the attacker as CSV. ## Example Exploit Flow 1. Authenticate normally. 2. Create or rename an asset using a name containing SQL metacharacters that closes the crosstab column definition. 3. Ensure the asset has an exportable datapoint attribute. 4. Write at least one datapoint value for that attribute, if necessary. 5. Request a CSV crosstab datapoint export for the crafted asset attribute. 6. Inspect the returned ZIP/CSV. The CSV contains both normal datapoint rows and rows produced by the injected SQL. A safe proof query demonstrated exfiltration of: - `current_user` - `current_database()` - `count(*)` from an application table The returned CSV contained a row equivalent to: ```text <timestamp>,<database_user>:<database_name>:<table_count> ``` ## Root Cause The root cause is manual SQL string construction using user-controlled display data as SQL syntax. The asset name is treated as presentation data in the application model, but later reused as part of executable SQL: - As an SQL identifier in the crosstab output column list. - As a value inside a category query string passed to PostgreSQL. These contexts require different escaping rules. Applying partial string escaping is error-prone and currently misses exploitable grammar boundaries. ## Recommended Fix Avoid embedding user-controlled asset names directly into executable SQL. Recommended options: 1. Do not use asset names as SQL identifiers. - Generate deterministic internal column aliases such as `c1`, `c2`, `c3`. - Keep the user-facing asset/attribute labels outside SQL and apply them only when serializing CSV headers. 2. If dynamic identifiers are unavoidable, quote them using a database-aware identifier quoting function. - For PostgreSQL identifiers, double embedded `"` characters. - Do not perform ad hoc quoting with string concatenation. 3. Avoid fixed dollar-quote delimiters around attacker-influenced content. - Use prepared statements or server-side functions where possible. - If textual SQL must be generated, choose a delimiter that cannot appear in user input or escape/validate before use. 4. Add a strict validation boundary for display names if the product can tolerate it. - This should be defense-in-depth, not the only fix. - Reject control characters and SQL-significant delimiter sequences in asset names if they are not required. 5. Add regression tests for: - Asset names containing `"`. - Asset names containing the fixed dollar-quote delimiter. - Asset names containing newline/comment syntax. - Crosstab exports with multiple assets and attributes. - Confirmation that returned CSV never contains injected query output. ## Suggested Safe Design Build the crosstab with internal, non-user-controlled category keys and column names. For example: - Use asset IDs and attribute names only as parameterized data for filtering. - Generate internal column identifiers such as `col_0`, `col_1`. - Maintain a separate mapping from `col_0` to the display label. - Replace the CSV header row after query execution using application-side serialization, not SQL identifiers derived from user input. This removes asset display names from SQL syntax entirely. ## Severity Suggested severity: High Rationale: - Network reachable through the authenticated API. - Low attack complexity after authentication. - Requires only ordinary asset read/write/export capabilities. - Demonstrated SQL injection result exfiltration through a normal application response. - High confidentiality impact due to possible cross-tenant database reads. - Integrity and availability impact were not required for the demonstrated exploit and should be assessed separately based on the database role's privileges.

GHSA-cgfv-jrfp-2r7v Affected Openremote manager Versions

1 component(s)
Fixed Versions:
1.26.0
Product / Package Ecosystem Affected Versions Fixed Version
io.openremote:openremote-manager
Maven
< 1.26.0
1.26.0

References

Frequently Asked Questions

What is GHSA-cgfv-jrfp-2r7v?
GHSA-cgfv-jrfp-2r7v is a security vulnerability in Openremote manager involving OpenRemote has Authenticated SQL Injection via Datapoint Crosstab Export under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
Which Openremote manager versions are affected?
Affected versions include releases prior to 1.26.0 across affected packages: io.openremote:openremote-manager.
What version fixes GHSA-cgfv-jrfp-2r7v?
Upgrading to 1.26.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is openremote-manager affected?
Yes, io.openremote:openremote-manager is affected in releases prior to 1.26.0. Upgrade your dependency to 1.26.0 or later.
What CWE is associated with GHSA-cgfv-jrfp-2r7v?
GHSA-cgfv-jrfp-2r7v is classified under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.).
How can I check Maven dependencies for GHSA-cgfv-jrfp-2r7v?
Run the dependency tree command: 'mvn dependency:tree -Dincludes=io.openremote'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-cgfv-jrfp-2r7v",
  "modified" : "2026-07-06T21:51:32Z",
  "published" : "2026-07-06T21:51:32Z",
  "aliases" : [ ],
  "summary" : "OpenRemote has Authenticated SQL Injection via Datapoint Crosstab Export",
  "details" : "## Summary\n\nThe datapoint export API builds a PostgreSQL crosstab export query by concatenating asset display names into raw SQL. An authenticated user who can create or rename an asset and then request a crosstab datapoint export can inject SQL through the asset name. The injected query output is streamed back to the caller inside the normal ZIP/CSV export response.\n\nThis creates a practical database exfiltration primitive through the application API. In a multi-tenant deployment, this can expose data outside the attacker's tenant if the application database role can read shared manager tables.\n\n## Affected Component\n\n- Datapoint export endpoint for asset datapoints.\n- Crosstab export formats, specifically CSV crosstab-style exports.\n- Query builder path that constructs a `COPY (SELECT ... FROM crosstab(...)) TO STDOUT` statement.\n\n## Security Impact\n\nImpact is high. A remote authenticated attacker with asset read/write capabilities can:\n\n- Store SQL syntax inside an asset name.\n- Trigger the crosstab export path for that asset.\n- Cause the backend to execute attacker-influenced SQL through the PostgreSQL connection used by the manager service.\n- Receive injected `SELECT` results in the exported CSV contained in the ZIP response.\n\nThe demonstrated impact is database data exfiltration. The proof of concept safely retrieved database execution context and an aggregate table count. A real attacker could adapt the injected `SELECT` to read other database tables accessible to the application database role.\n\nThis is especially sensitive in multi-tenant deployments because application tables commonly contain data for multiple realms/tenants in the same database.\n\n## Attack Preconditions\n\nThe attacker needs:\n\n- A valid authenticated session.\n- Permission to create or rename at least one asset.\n- Permission to read/export datapoints for at least one attribute on that asset.\n- Access to a crosstab datapoint export format.\n\nNo direct database access is required. No server filesystem access is required. No token forgery is required.\n\n## Technical Details\n\nThe export implementation derives a crosstab header from the asset name and attribute name. It then embeds that header into two SQL contexts:\n\n1. A PostgreSQL double-quoted column identifier:\n\n```sql\n\"<asset name> : <attribute name>\" text\n```\n\n2. A category query passed to `crosstab(...)`, wrapped in a fixed dollar-quoted delimiter:\n\n```sql\n$cat$ SELECT header FROM (VALUES ('<asset name> : <attribute name>')) AS t(header) $cat$\n```\n\nThe current escaping is incomplete:\n\n- Single quotes are escaped in one string-literal context.\n- Double quotes in asset names are not escaped before being placed inside quoted identifiers.\n- The fixed dollar-quote delimiter is not protected against an asset name containing the delimiter token.\n\nAs a result, an attacker-controlled asset name can break out of the intended SQL grammar boundary and append SQL to the generated `COPY ... TO STDOUT` query. Because the backend streams `COPY` output into the export response, injected query rows are returned to the attacker as CSV.\n\n## Example Exploit Flow\n\n1. Authenticate normally.\n2. Create or rename an asset using a name containing SQL metacharacters that closes the crosstab column definition.\n3. Ensure the asset has an exportable datapoint attribute.\n4. Write at least one datapoint value for that attribute, if necessary.\n5. Request a CSV crosstab datapoint export for the crafted asset attribute.\n6. Inspect the returned ZIP/CSV. The CSV contains both normal datapoint rows and rows produced by the injected SQL.\n\nA safe proof query demonstrated exfiltration of:\n\n- `current_user`\n- `current_database()`\n- `count(*)` from an application table\n\nThe returned CSV contained a row equivalent to:\n\n```text\n<timestamp>,<database_user>:<database_name>:<table_count>\n```\n\n## Root Cause\n\nThe root cause is manual SQL string construction using user-controlled display data as SQL syntax.\n\nThe asset name is treated as presentation data in the application model, but later reused as part of executable SQL:\n\n- As an SQL identifier in the crosstab output column list.\n- As a value inside a category query string passed to PostgreSQL.\n\nThese contexts require different escaping rules. Applying partial string escaping is error-prone and currently misses exploitable grammar boundaries.\n\n## Recommended Fix\n\nAvoid embedding user-controlled asset names directly into executable SQL.\n\nRecommended options:\n\n1. Do not use asset names as SQL identifiers.\n   - Generate deterministic internal column aliases such as `c1`, `c2`, `c3`.\n   - Keep the user-facing asset/attribute labels outside SQL and apply them only when serializing CSV headers.\n\n2. If dynamic identifiers are unavoidable, quote them using a database-aware identifier quoting function.\n   - For PostgreSQL identifiers, double embedded `\"` characters.\n   - Do not perform ad hoc quoting with string concatenation.\n\n3. Avoid fixed dollar-quote delimiters around attacker-influenced content.\n   - Use prepared statements or server-side functions where possible.\n   - If textual SQL must be generated, choose a delimiter that cannot appear in user input or escape/validate before use.\n\n4. Add a strict validation boundary for display names if the product can tolerate it.\n   - This should be defense-in-depth, not the only fix.\n   - Reject control characters and SQL-significant delimiter sequences in asset names if they are not required.\n\n5. Add regression tests for:\n   - Asset names containing `\"`.\n   - Asset names containing the fixed dollar-quote delimiter.\n   - Asset names containing newline/comment syntax.\n   - Crosstab exports with multiple assets and attributes.\n   - Confirmation that returned CSV never contains injected query output.\n\n## Suggested Safe Design\n\nBuild the crosstab with internal, non-user-controlled category keys and column names. For example:\n\n- Use asset IDs and attribute names only as parameterized data for filtering.\n- Generate internal column identifiers such as `col_0`, `col_1`.\n- Maintain a separate mapping from `col_0` to the display label.\n- Replace the CSV header row after query execution using application-side serialization, not SQL identifiers derived from user input.\n\nThis removes asset display names from SQL syntax entirely.\n\n## Severity\n\nSuggested severity: High\n\nRationale:\n\n- Network reachable through the authenticated API.\n- Low attack complexity after authentication.\n- Requires only ordinary asset read/write/export capabilities.\n- Demonstrated SQL injection result exfiltration through a normal application response.\n- High confidentiality impact due to possible cross-tenant database reads.\n- Integrity and availability impact were not required for the demonstrated exploit and should be assessed separately based on the database role's privileges.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:L/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Maven",
      "name" : "io.openremote:openremote-manager"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "1.26.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/openremote/openremote/security/advisories/GHSA-cgfv-jrfp-2r7v"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/openremote/openremote/commit/02ac83074b81617add814b2a72d459abdf374147"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/openremote/openremote"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-89" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-06T21:51:32Z",
    "nvd_published_at" : null
  }
}