# OpenRemote has Authenticated SQL Injection via Datapoint Crosstab Export

Authenticated SQL injection in OpenRemote's crosstab datapoint export: asset display names concatenated into PostgreSQL COPY/crosstab SQL let attackers exfiltrate database data via the CSV export.

- Payout: Research
- Severity: high
- Program: OpenRemote
- Found by: aramosf
- Disclosed: 2026-07-06
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-cgfv-jrfp-2r7v
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-cgfv-jrfp-2r7v
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:java technique:second-order technique:source-review vuln:sqli

**Target:** Datapoint crosstab export endpoint (PostgreSQL COPY/crosstab query builder)
**Root cause:** Asset display names are concatenated into raw SQL as both a double-quoted column identifier and inside a fixed dollar-quoted category query; double quotes and the dollar-quote delimiter are not escaped, allowing grammar breakout.
**Steps:**
1. Authenticate normally.
2. Create or rename an asset whose name contains SQL metacharacters that close the crosstab column definition (e.g. a `"` breakout and the `$cat$` delimiter token).
3. Ensure the asset has an exportable datapoint attribute and write at least one datapoint value.
4. Request a CSV crosstab datapoint export for the crafted asset attribute.
5. Inspect the returned ZIP/CSV: injected SELECT rows appear alongside normal datapoint rows, e.g. `<timestamp>,<database_user>:<database_name>:<table_count>`.
**How found:** Code review of the export query builder: noticed asset display name reused as SQL identifier and inside a dollar-quoted category query, with only single-quote escaping applied and double quotes/delimiter unprotected.
**Exploitation:** Attacker stores SQL in an asset name, triggers the crosstab export which builds `COPY (SELECT ... FROM crosstab(...)) TO STDOUT`, and the injected SELECT output is streamed back inside the normal ZIP/CSV export response. PoC exfiltrated current_user, current_database() and a table count; adaptable to read other tables the DB role can access, including cross-tenant data.
**Escalation:** Cross-tenant data exposure in multi-tenant deployments since shared manager tables hold multiple realms' data under one DB role.
**Insight:** Display names reused as SQL identifiers are a recurring injection source; check both quoted-identifier contexts (unescaped `"`) and dollar-quoted literals (attacker-supplied delimiter token) in export/report builders, and note that COPY ... TO STDOUT turns blind SQLi into direct exfil through the normal response.

