GHSA-r2f4-ff2p-xc64: Pimcore Platform - SQL Injection in DataObject composite index handling during class definition import/save
DescriptionAn 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.VulnerabilityRoot causeSource:`Pimcore\Model\DataObject\ClassDefinition\Service::importClassDefinitionFromJson()` accepts `compositeIndices` directly from imported JSON.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.Sink:`Pimcore\Model\DataObject\Traits\CompositeIndexTrait::updateCompositeIndices()` builds raw SQL with string concatenation and executes it via `$this->db->executeQuery(...)`.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`importClassDefinitionFromJson()` decodes attacker-controlled JSON and forwards `compositeIndices`.`setCompositeIndices()` stores those values without sanitizing identifier content.`ClassDefinition::save()` reaches `ClassDefinition\Dao::update()`.`Dao::update()` calls `updateCompositeIndices()` for:`object_store_<classId>``object_query_<classId>``Localizedfield\Dao` also calls `updateCompositeIndices()` for:localized query tableslocalized store tablesWhy this is exploitableThe vulnerable `ADD INDEX` statement is built as:`$columnName` is produced from `implode(',', $columns)` and is not quoted or validated. A malicious `index_columns` element such as:produces SQL of the form: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.ImpactThe 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 tablesbackend denial of service by breaking expected table layoutdata 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`PoCApplication-level PoCPreconditions:valid authenticated administrative sessionability to import or save a class definition containing `compositeIndices`The original report reproduced the issue through an authenticated Studio endpoint:Minimal malicious JSON fragment:Reproduction:Authenticate as an administrator with permission to manage/import class definitions.Export an existing class definition or prepare a valid class-definition JSON document.Replace only the `compositeIndices` section with the payload above.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 confirmationThe behavior is directly visible from the code path:No escaping or allowlist validation is applied to `$columns` before they are interpolated into SQL.Evidence of ExploitationVideo of exploitation:https://github.com/user-attachments/assets/64a49147-12a5-4550-ba22-cb4383523557Static evidence:System InformationPimcore Platform
Version `v12.3.3`
Database layer: `doctrine/dbal` `^4.4`
Operating System: AnyResourcesGithub Repository: https://github.com/pimcore/pimcore
Security: https://github.com/pimcore/pimcore/securityThis 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).
CVSS v4.0: 7