+ "details": "## Description\nAn 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.\n\nThe 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.\n\nAlthough 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.\n\n## Vulnerability\n### Root cause\n1. Source:\n - `Pimcore\\Model\\DataObject\\ClassDefinition\\Service::importClassDefinitionFromJson()` accepts `compositeIndices` directly from imported JSON.\n2. Assignment:\n - `Pimcore\\Model\\DataObject\\ClassDefinition::setCompositeIndices()` does not enforce an allowlist for index names or column names.\n - The only special handling is a ManyToOne relation rewrite to `__id` and `__type`, which is not a security control.\n3. Sink:\n - `Pimcore\\Model\\DataObject\\Traits\\CompositeIndexTrait::updateCompositeIndices()` builds raw SQL with string concatenation and executes it via `$this->db->executeQuery(...)`.\n4. Missing protection:\n - `quoteIdentifier()` is used for the `SHOW INDEXES` query, but not for the dynamic `ALTER TABLE` statements.\n - No server-side schema validation restricts `index_key` or `index_columns` to known safe identifier characters.\n\n### Confirmed source-to-sink path\n1. `importClassDefinitionFromJson()` decodes attacker-controlled JSON and forwards `compositeIndices`.\n2. `setCompositeIndices()` stores those values without sanitizing identifier content.\n3. `ClassDefinition::save()` reaches `ClassDefinition\\Dao::update()`.\n4. `Dao::update()` calls `updateCompositeIndices()` for:\n - `object_store_<classId>`\n - `object_query_<classId>`\n5. `Localizedfield\\Dao` also calls `updateCompositeIndices()` for:\n - localized query tables\n - localized store tables\n\n### Why this is exploitable\nThe vulnerable `ADD INDEX` statement is built as:\n\n```php\n'ALTER TABLE `'.$table.'` ADD INDEX `' . $key.'` ('.$columnName.');'\n```\n\n`$columnName` is produced from `implode(',', $columns)` and is not quoted or validated. A malicious `index_columns` element such as:\n\n```text\nslider), DROP COLUMN `oo_className` -- \n```\n\nproduces SQL of the form:\n\n```sql\nALTER TABLE `object_query_<id>` ADD INDEX `c_poc_idx` (slider), DROP COLUMN `oo_className` -- );\n```\n\nThis 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.\n\n### Impact\nThe 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.\n\nPractical impact includes:\n- unauthorized schema modification on object query/store tables\n- backend denial of service by breaking expected table layout\n- data integrity impact for DataObject storage and queries\n\n`index_key` is also concatenated into SQL without proper identifier escaping, but the most defensible exploitation path is through `index_columns`.\n\nRelevant code:\n- `models/DataObject/ClassDefinition/Service.php:92-137`\n- `models/DataObject/ClassDefinition.php:994-1006`\n- `models/DataObject/Traits/CompositeIndexTrait.php:30-85`\n- `models/DataObject/ClassDefinition/Dao.php:217-218`\n- `models/DataObject/Localizedfield/Dao.php:945-951`\n\n## PoC\n### Application-level PoC\nPreconditions:\n- valid authenticated administrative session\n- ability to import or save a class definition containing `compositeIndices`\n\nThe original report reproduced the issue through an authenticated Studio endpoint:\n\n```http\nPOST /pimcore-studio/api/class/definition/configuration-view/detail/1/import\n```\n\nMinimal malicious JSON fragment:\n\n```json\n{\n \"compositeIndices\": [\n {\n \"index_key\": \"poc_idx\",\n \"index_type\": \"query\",\n \"index_columns\": [\n \"slider), DROP COLUMN `oo_className` -- \"\n ]\n }\n ]\n}\n```\n\nReproduction:\n1. Authenticate as an administrator with permission to manage/import class definitions.\n2. Export an existing class definition or prepare a valid class-definition JSON document.\n3. Replace only the `compositeIndices` section with the payload above.\n4. Import the modified definition or save the class through the administrative workflow.\n\nExpected result:\n- Pimcore reaches `updateCompositeIndices()` during class save/import.\n- The backend executes an attacker-influenced `ALTER TABLE` statement against the target object table.\n- The affected class table is modified unexpectedly, for example by dropping a column or otherwise changing schema.\n\n### Minimal source-level confirmation\nThe behavior is directly visible from the code path:\n\n```php\n$newIndicesMap['c_' . $key] = implode(',', $columns);\n$columnName = $newIndicesMap[$key];\n$this->db->executeQuery(\n 'ALTER TABLE `'.$table.'` ADD INDEX `' . $key.'` ('.$columnName.');'\n);\n```\n\nNo escaping or allowlist validation is applied to `$columns` before they are interpolated into SQL.\n\n## Evidence of Exploitation\n\n- Video of exploitation:\n\nhttps://github.com/user-attachments/assets/64a49147-12a5-4550-ba22-cb4383523557\n\n- Static evidence:\n\n<img width=\"3004\" height=\"1686\" alt=\"Dragons-img\" src=\"https://github.com/user-attachments/assets/2e920636-ce7e-4f8b-b80c-88fb3c4c5299\" />\n\n\n## System Information\nPimcore Platform\nVersion `v12.3.3`\nDatabase layer: `doctrine/dbal` `^4.4`\nOperating System: Any\n\n## Resources\nGithub Repository: https://github.com/pimcore/pimcore\nSecurity: https://github.com/pimcore/pimcore/security\n\nThis issue was discovered by Oscar Uribe, Security Researcher at Fluid Attacks, who reached out to Pimcore.\n\nAs 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.\n\n\nThe 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).",
0 commit comments