VDB
KO
HIGH

GHSA-r2f4-ff2p-xc64

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

Details

## Description An authenticated administrative user who can import or save DataObject class definitions can inject attacker-controlled composite index metadata and trigger unintended SQL execution in the backend.

The vulnerable flow accepts `compositeIndices` from imported JSON, stores the values without strict validation, and later concatenates them directly into `ALTER TABLE ... DROP INDEX` and `ALTER TABLE ... ADD INDEX` statements executed through Doctrine DBAL.

Although the original report focused on `compositeIndices.index_key`, independent code review shows that the strongest and most reliable injection point is `compositeIndices.index_columns`, because it is inserted verbatim inside the `ADD INDEX (...)` clause. This permits injection of additional `ALTER TABLE` subclauses against Pimcore object tables without relying on stacked queries.

## Vulnerability ### Root cause 1. Source: - `Pimcore\Model\DataObject\ClassDefinition\Service::importClassDefinitionFromJson()` accepts `compositeIndices` directly from imported JSON. 2. Assignment: - `Pimcore\Model\DataObject\ClassDefinition::setCompositeIndices()` does not enforce an allowlist for index names or column names. - The only special handling is a ManyToOne relation rewrite to `__id` and `__type`, which is not a security control. 3. Sink: - `Pimcore\Model\DataObject\Traits\CompositeIndexTrait::updateCompositeIndices()` builds raw SQL with string concatenation and executes it via `$this->db->executeQuery(...)`. 4. Missing protection: - `quoteIdentifier()` is used for the `SHOW INDEXES` query, but not for the dynamic `ALTER TABLE` statements. - No server-side schema validation restricts `index_key` or `index_columns` to known safe identifier characters.

### Confirmed source-to-sink path 1. `importClassDefinitionFromJson()` decodes attacker-controlled JSON and forwards `compositeIndices`. 2. `setCompositeIndices()` stores those values without sanitizing identifier content. 3. `ClassDefinition::save()` reaches `ClassDefinition\Dao::update()`. 4. `Dao::update()` calls `updateCompositeIndices()` for: - `object_store_<classId>` - `object_query_<classId>` 5. `Localizedfield\Dao` also calls `updateCompositeIndices()` for: - localized query tables - localized store tables

### Why this is exploitable The vulnerable `ADD INDEX` statement is built as:

```php 'ALTER TABLE `'.$table.'` ADD INDEX `' . $key.'` ('.$columnName.');' ```

`$columnName` is produced from `implode(',', $columns)` and is not quoted or validated. A malicious `index_columns` element such as:

```text slider), DROP COLUMN `oo_className` -- ```

produces SQL of the form:

```sql ALTER TABLE `object_query_<id>` ADD INDEX `c_poc_idx` (slider), DROP COLUMN `oo_className` -- ); ```

This remains a single `ALTER TABLE` statement, so the base vulnerability does not depend on multi-statement support. The attacker can inject additional DDL clauses affecting the target Pimcore object table.

### Impact The issue allows a privileged attacker to alter backend SQL behavior during class-definition import/save and modify schema on Pimcore object tables associated with the affected class.

Practical impact includes: - unauthorized schema modification on object query/store tables - backend denial of service by breaking expected table layout - data integrity impact for DataObject storage and queries

`index_key` is also concatenated into SQL without proper identifier escaping, but the most defensible exploitation path is through `index_columns`.

Relevant code: - `models/DataObject/ClassDefinition/Service.php:92-137` - `models/DataObject/ClassDefinition.php:994-1006` - `models/DataObject/Traits/CompositeIndexTrait.php:30-85` - `models/DataObject/ClassDefinition/Dao.php:217-218` - `models/DataObject/Localizedfield/Dao.php:945-951`

## PoC ### Application-level PoC Preconditions: - valid authenticated administrative session - ability to import or save a class definition containing `compositeIndices`

The original report reproduced the issue through an authenticated Studio endpoint:

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

Minimal malicious JSON fragment:

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

Reproduction: 1. Authenticate as an administrator with permission to manage/import class definitions. 2. Export an existing class definition or prepare a valid class-definition JSON document. 3. Replace only the `compositeIndices` section with the payload above. 4. Import the modified definition or save the class through the administrative workflow.

Expected result: - Pimcore reaches `updateCompositeIndices()` during class save/import. - The backend executes an attacker-influenced `ALTER TABLE` statement against the target object table. - The affected class table is modified unexpectedly, for example by dropping a column or otherwise changing schema.

### Minimal source-level confirmation The behavior is directly visible from the code path:

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

No escaping or allowlist validation is applied to `$columns` before they are interpolated into SQL.

## Evidence of Exploitation

- Video of exploitation:

https://github.com/user-attachments/assets/64a49147-12a5-4550-ba22-cb4383523557

- Static evidence:

<img width="3004" height="1686" alt="Dragons-img" src="https://github.com/user-attachments/assets/2e920636-ce7e-4f8b-b80c-88fb3c4c5299" />

## System Information Pimcore Platform Version `v12.3.3` Database layer: `doctrine/dbal` `^4.4` Operating System: Any

## Resources Github Repository: https://github.com/pimcore/pimcore Security: https://github.com/pimcore/pimcore/security

This issue was discovered by Oscar Uribe, Security Researcher at Fluid Attacks, who reached out to Pimcore.

As part of standard disclosure measures, Fluid Attacks follows a timeline (outlined at https://fluidattacks.com/advisories/policy) that is aligned with ISO/IEC 29147:2018 and ISO/IEC 30111:2019. In short, the timeline works as follows: Fluid Attacks requests acknowledgment of the report within a few days of project maintainers first accessing it, and from there, Fluid Attacks is glad to coordinate a joint disclosure date with the affected party, typically within 90 days of the initial discovery. This allows the maintainers' team reasonable time to assess, develop, and release a fix.

The CVE ID **"CVE-2026-5394"** has been reserved for this issue, and the advisory will eventually be published at [https://fluidattacks.com/advisories/dragons](https://fluidattacks.com/advisories/dragons).

Are you affected?

Enter the version of the package you're using.

Affected packages

Packagist / pimcore/pimcore
Introduced in: 0 Fixed in: 12.3.7
Fix composer require pimcore/pimcore:^12.3.7

References