# Pimcore Platform - SQL Injection in DataObject composite index handling during class definition import/save

Admin-supplied compositeIndices.index_columns in Pimcore class-definition import is concatenated into ALTER TABLE ADD INDEX, allowing injected DDL clauses against object tables.

- Payout: Research
- Severity: high
- Program: Pimcore
- Found by (vendor credit): Oscar Naveda, Fluid Attacks' AI SAST Scanner
- Found by: researchatfluidattacks, kingjia90
- Disclosed: 2026-05-28
- CVE: CVE-2026-5394
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-r2f4-ff2p-xc64
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-r2f4-ff2p-xc64
- Tags: area:web domain:web impact:dos impact:integrity kind:research platform:ghsa sev:high tech:php technique:source-review vuln:privilege-escalation vuln:sqli

**Target:** Pimcore Studio class-definition import endpoint / DataObject composite index handling
**Root cause:** importClassDefinitionFromJson accepts compositeIndices without validation; setCompositeIndices stores index_columns verbatim and CompositeIndexTrait::updateCompositeIndices concatenates them into ALTER TABLE ADD INDEX without quoting or allowlisting.
**Steps:**
1. Authenticate as admin with class-definition management permission.
2. Prepare a valid class-definition JSON and replace compositeIndices with payload: `{"index_key":"poc_idx","index_type":"query","index_columns":["slider), DROP COLUMN `oo_className` -- "]}`.
3. POST it to `/pimcore-studio/api/class/definition/configuration-view/detail/1/import` (or save class via admin workflow).
4. ClassDefinition::save -> Dao::update -> updateCompositeIndices builds `ALTER TABLE `object_query_<id>` ADD INDEX `c_poc_idx` (slider), DROP COLUMN `oo_className` -- );` and executes it.
5. Observe schema change on the object query/store table.
**How found:** Source review of the import-to-save path; noted quoteIdentifier was applied to SHOW INDEXES but not to dynamic ALTER TABLE statements, and that index_columns is imploded verbatim into the ADD INDEX clause, making it a more reliable injection point than index_key.
**Exploitation:** Privileged admin imports a crafted class definition; the unquoted index_columns value breaks out of the ADD INDEX column list and appends additional ALTER TABLE DDL subclauses (e.g. DROP COLUMN) executed against object_query_/object_store_ tables, no stacked queries needed.
**Insight:** When auditing schema-migration code, check whether identifier quoting is applied consistently: a quoted SHOW INDEXES query next to an unquoted ALTER TABLE ADD INDEX is a strong signal, and column-list positions inside parentheses allow clause injection without stacked queries.

## Proof of concept (copied exactly from the original report)

**Malicious compositeIndices JSON payload with SQL injection**

```
{
  "compositeIndices": [
    {
      "index_key": "poc_idx",
      "index_type": "query",
      "index_columns": [
        "slider), DROP COLUMN `oo_className` -- "
      ]
    }
  ]
}
```

**Vulnerable ADD INDEX SQL concatenation sink**

```
$newIndicesMap['c_' . $key] = implode(',', $columns);
$columnName = $newIndicesMap[$key];
$this->db->executeQuery(
    'ALTER TABLE `'.$table.'` ADD INDEX `' . $key.'` ('.$columnName.');'
);
```

**Import endpoint used to deliver payload**

```
POST /pimcore-studio/api/class/definition/configuration-view/detail/1/import
```

