GHSA-R2F4-FF2P-XC64

Vulnerability from github – Published: 2026-05-28 20:47 – Updated: 2026-05-28 20:47
VLAI
Summary
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:
  2. Pimcore\Model\DataObject\ClassDefinition\Service::importClassDefinitionFromJson() accepts compositeIndices directly from imported JSON.
  3. Assignment:
  4. Pimcore\Model\DataObject\ClassDefinition::setCompositeIndices() does not enforce an allowlist for index names or column names.
  5. The only special handling is a ManyToOne relation rewrite to __id and __type, which is not a security control.
  6. Sink:
  7. Pimcore\Model\DataObject\Traits\CompositeIndexTrait::updateCompositeIndices() builds raw SQL with string concatenation and executes it via $this->db->executeQuery(...).
  8. Missing protection:
  9. quoteIdentifier() is used for the SHOW INDEXES query, but not for the dynamic ALTER TABLE statements.
  10. 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:
  5. object_store_<classId>
  6. object_query_<classId>
  7. Localizedfield\Dao also calls updateCompositeIndices() for:
  8. localized query tables
  9. localized store tables

Why this is exploitable

The vulnerable ADD INDEX statement is built as:

'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:

slider), DROP COLUMN `oo_className` -- 

produces SQL of the form:

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:

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

Minimal malicious JSON fragment:

{
  "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:

$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:

Dragons-img

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.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 12.3.6"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "pimcore/pimcore"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "12.3.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-5394"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-89"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-28T20:47:10Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "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-\u003edb-\u003eexecuteQuery(...)`.\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_\u003cclassId\u003e`\n   - `object_query_\u003cclassId\u003e`\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\u0027ALTER TABLE `\u0027.$table.\u0027` ADD INDEX `\u0027 . $key.\u0027` (\u0027.$columnName.\u0027);\u0027\n```\n\n`$columnName` is produced from `implode(\u0027,\u0027, $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_\u003cid\u003e` 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[\u0027c_\u0027 . $key] = implode(\u0027,\u0027, $columns);\n$columnName = $newIndicesMap[$key];\n$this-\u003edb-\u003eexecuteQuery(\n    \u0027ALTER TABLE `\u0027.$table.\u0027` ADD INDEX `\u0027 . $key.\u0027` (\u0027.$columnName.\u0027);\u0027\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\u003cimg width=\"3004\" height=\"1686\" alt=\"Dragons-img\" src=\"https://github.com/user-attachments/assets/2e920636-ce7e-4f8b-b80c-88fb3c4c5299\" /\u003e\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\u0027 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).",
  "id": "GHSA-r2f4-ff2p-xc64",
  "modified": "2026-05-28T20:47:10Z",
  "published": "2026-05-28T20:47:10Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/pimcore/pimcore/security/advisories/GHSA-r2f4-ff2p-xc64"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-5394"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pimcore/pimcore/pull/19108"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pimcore/pimcore/commit/6df625ff74015dc11f4bbe76170ce45bbd5dd61d"
    },
    {
      "type": "WEB",
      "url": "https://fluidattacks.com/es/advisories/dragons"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/pimcore/pimcore"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pimcore/pimcore/releases/tag/v12.3.7"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:L/VA:L/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Pimcore Platform - SQL Injection in DataObject composite index handling during class definition import/save"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…