[GHSA-h4ph-crvj-9h92] Pimcore Admin Classic Bundle Vulnerable to SQL Injection in Translation Grid Date Filter via Unsanitized Property Parameter#7899
Conversation
|
Hi there @kingjia90! A community member has suggested an improvement to your security advisory. If approved, this change will affect the global advisory listed at github.com/advisories. It will not affect the version listed in your project repository. This change will be reviewed by our Security Curation Team. If you have thoughts or feedback, please share them in a comment here! If this PR has already been closed, you can start a new community contribution for this advisory |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates a GitHub-reviewed advisory entry for GHSA-h4ph-crvj-9h92, revising the write-up and changing the affected/fixed version metadata.
Changes:
- Updated the advisory
detailsmarkdown content (removing “Draft” wording and reformatting the narrative). - Changed the fixed version and last-known-affected version range from the 2.3.x line to the 1.7.x line.
- Bumped the
modifiedtimestamp.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ], | ||
| "summary": "Pimcore Admin Classic Bundle Vulnerable to SQL Injection in Translation Grid Date Filter via Unsanitized Property Parameter", | ||
| "details": "# GitHub Security Advisory Draft — GM-369\n\n## Summary\nSQL injection in Pimcore's translation grid date filter — the user-supplied `property` field from the filter JSON is interpolated directly into a `UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(...)))` SQL expression without parameterization or allowlist validation.\n\n## Affected Component\n- **Package:** `pimcore/admin-ui-classic-bundle`\n- **File:** `src/Controller/Admin/TranslationController.php`\n- **Lines:** 565 (input), 569 (inadequate sanitization), 593 (injection point)\n- **Endpoint:** `POST /admin/translation/translations`\n\n## Description\nThe translation grid endpoint processes JSON filter parameters. When a filter has `type: \"date\"`, the `property` field is extracted and used to construct a SQL expression:\n\n```php\n$fieldname = $filter[$propertyField]; // Line 565 — user input\n$fieldname = str_replace('--', '', $fieldname); // Line 569 — trivially bypassable\n$fieldname = $tableName . '.' . $fieldname; // Line 577\n$fieldname = \"UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))\"; // Line 593 — injection\n```\n\nThe `str_replace('--', '')` sanitization is trivially bypassable (use `/**/` comments or `----`). In non-language mode, `$fieldname` is concatenated directly into the SQL condition without quoting or parameterization.\n\n## Impact\nAuthenticated user with translations view permission can extract arbitrary database data via UNION-based or error-based SQL injection. Combined with GM-249 (unsafe unserialize), this enables an SQLi → deserialization → RCE chain.\n\n## Proof of Concept\n```\nPOST /admin/translation/translations\nfilter=[{\"property\":\"1))) UNION SELECT password FROM users WHERE ((1\",\"type\":\"date\",\"operator\":\"eq\",\"value\":\"2026-01-01\"}]\n```\n\n## Suggested Fix\nValidate `$fieldname` against an allowlist of valid column names before SQL interpolation:\n```php\n$allowedDateColumns = ['creationDate', 'modificationDate'];\nif (!in_array($fieldname, $allowedDateColumns, true)) {\n continue;\n}\n```\n\n## References\n- CWE-89: SQL Injection\n- Related: CVE-2026-27461 (RLIKE injection in Dependency/Dao.php — different code path)\n\n\n---\n\n## Suggested Fix\n\nIn `TranslationController.php`: (1) Add allowlist check for non-language fieldnames before processing. (2) Replace raw string interpolation `UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))` with `$db->quoteIdentifier($fieldname)` to prevent SQL injection in date filter expressions.\n\n```diff\n--- a/src/Controller/Admin/TranslationController.php\n+++ b/src/Controller/Admin/TranslationController.php\n@@ -569,7 +569,15 @@ class TranslationController extends AdminAbstractController\n $fieldname = str_replace('--', '', $fieldname);\n \n if (!$languageMode && in_array($fieldname, $validLanguages)\n || $languageMode && !in_array($fieldname, $validLanguages)) {\n continue;\n }\n \n+ // Allowlist non-language fieldnames to prevent SQL injection\n+ $allowedNonLanguageFields = ['key', 'type', 'creationDate', 'modificationDate'];\n+ if (!$languageMode && !in_array($fieldname, $allowedNonLanguageFields) && !in_array($fieldname, $validLanguages)) {\n+ continue;\n+ }\n+\n if (!$languageMode) {\n $fieldname = $tableName . '.' . $fieldname;\n }\n@@ -582,7 +590,7 @@ class TranslationController extends AdminAbstractController\n } elseif ($filter[$operatorField] == 'eq') {\n $operator = '=';\n- $fieldname = \"UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))\";\n+ // Use validated fieldname only — never interpolate raw user input into SQL functions\n+ $fieldname = sprintf('UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(%s)))', $db->quoteIdentifier($fieldname));\n }\n\n```\n\n---\n\n## Proposed Fix\n\n```diff\n--- a/src/Controller/Admin/TranslationController.php\n+++ b/src/Controller/Admin/TranslationController.php\n@@ -569,7 +569,15 @@ class TranslationController extends AdminAbstractController\n $fieldname = str_replace('--', '', $fieldname);\n \n if (!$languageMode && in_array($fieldname, $validLanguages)\n || $languageMode && !in_array($fieldname, $validLanguages)) {\n continue;\n }\n \n+ // Allowlist non-language fieldnames to prevent SQL injection\n+ $allowedNonLanguageFields = ['key', 'type', 'creationDate', 'modificationDate'];\n+ if (!$languageMode && !in_array($fieldname, $allowedNonLanguageFields) && !in_array($fieldname, $validLanguages)) {\n+ continue;\n+ }\n+\n if (!$languageMode) {\n $fieldname = $tableName . '.' . $fieldname;\n }\n@@ -582,7 +590,7 @@ class TranslationController extends AdminAbstractController\n } elseif ($filter[$operatorField] == 'eq') {\n $operator = '=';\n- $fieldname = \"UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))\";\n+ // Use validated fieldname only — never interpolate raw user input into SQL functions\n+ $fieldname = sprintf('UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(%s)))', $db->quoteIdentifier($fieldname));\n }\n```\n\nHappy to submit this as a PR against a private fork if that is the preferred workflow.", | ||
| "details": "# GM-369\n\n## Summary\nSQL injection in Pimcore's translation grid date filter — the user-supplied `property` field from the filter JSON is interpolated directly into a `UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(...)))` SQL expression without parameterization or allowlist validation.\n\n## Affected Component\n- **Package:** `pimcore/admin-ui-classic-bundle`\n- **File:** `src/Controller/Admin/TranslationController.php`\n- **Lines:** 565 (input), 569 (inadequate sanitization), 593 (injection point)\n- **Endpoint:** `POST /admin/translation/translations`\n\n## Description\nThe translation grid endpoint processes JSON filter parameters. When a filter has `type: \"date\"`, the `property` field is extracted and used to construct a SQL expression:\n\n```php\n$fieldname = $filter[$propertyField]; // Line 565 — user input\n$fieldname = str_replace('--', '', $fieldname); // Line 569 — trivially bypassable\n$fieldname = $tableName . '.' . $fieldname; // Line 577\n$fieldname = \"UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))\"; // Line 593 — injection\n```\n\nThe `str_replace('--', '')` sanitization is trivially bypassable (use `/**/` comments or `----`). In non-language mode, `$fieldname` is concatenated directly into the SQL condition without quoting or parameterization.\n\n## Impact\nAuthenticated user with translations view permission can extract arbitrary database data via UNION-based or error-based SQL injection. Combined with GM-249 (unsafe unserialize), this enables an SQLi → deserialization → RCE chain.\n\n## Proof of Concept\n```\nPOST /admin/translation/translations\nfilter=[{\"property\":\"1))) UNION SELECT password FROM users WHERE ((1\",\"type\":\"date\",\"operator\":\"eq\",\"value\":\"2026-01-01\"}]\n```\n\n## Suggested Fix\nValidate `$fieldname` against an allowlist of valid column names before SQL interpolation:\n```php\n$allowedDateColumns = ['creationDate', 'modificationDate'];\nif (!in_array($fieldname, $allowedDateColumns, true)) {\n continue;\n}\n```\n\n## References\n- CWE-89: SQL Injection\n- Related: CVE-2026-27461 (RLIKE injection in Dependency/Dao.php — different code path)\n\n\n---\n\n## Suggested Fix\n\nIn `TranslationController.php`: (1) Add allowlist check for non-language fieldnames before processing. (2) Replace raw string interpolation `UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))` with `$db->quoteIdentifier($fieldname)` to prevent SQL injection in date filter expressions.\n\n```diff\n--- a/src/Controller/Admin/TranslationController.php\n+++ b/src/Controller/Admin/TranslationController.php\n@@ -569,7 +569,15 @@ class TranslationController extends AdminAbstractController\n $fieldname = str_replace('--', '', $fieldname);\n \n if (!$languageMode && in_array($fieldname, $validLanguages)\n || $languageMode && !in_array($fieldname, $validLanguages)) {\n continue;\n }\n \n+ // Allowlist non-language fieldnames to prevent SQL injection\n+ $allowedNonLanguageFields = ['key', 'type', 'creationDate', 'modificationDate'];\n+ if (!$languageMode && !in_array($fieldname, $allowedNonLanguageFields) && !in_array($fieldname, $validLanguages)) {\n+ continue;\n+ }\n+\n if (!$languageMode) {\n $fieldname = $tableName . '.' . $fieldname;\n }\n@@ -582,7 +590,7 @@ class TranslationController extends AdminAbstractController\n } elseif ($filter[$operatorField] == 'eq') {\n $operator = '=';\n- $fieldname = \"UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))\";\n+ // Use validated fieldname only — never interpolate raw user input into SQL functions\n+ $fieldname = sprintf('UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(%s)))', $db->quoteIdentifier($fieldname));\n }\n\n```\n\n---\n\n## Proposed Fix\n\n```diff\n--- a/src/Controller/Admin/TranslationController.php\n+++ b/src/Controller/Admin/TranslationController.php\n@@ -569,7 +569,15 @@ class TranslationController extends AdminAbstractController\n $fieldname = str_replace('--', '', $fieldname);\n \n if (!$languageMode && in_array($fieldname, $validLanguages)\n || $languageMode && !in_array($fieldname, $validLanguages)) {\n continue;\n }\n \n+ // Allowlist non-language fieldnames to prevent SQL injection\n+ $allowedNonLanguageFields = ['key', 'type', 'creationDate', 'modificationDate'];\n+ if (!$languageMode && !in_array($fieldname, $allowedNonLanguageFields) && !in_array($fieldname, $validLanguages)) {\n+ continue;\n+ }\n+\n if (!$languageMode) {\n $fieldname = $tableName . '.' . $fieldname;\n }\n@@ -582,7 +590,7 @@ class TranslationController extends AdminAbstractController\n } elseif ($filter[$operatorField] == 'eq') {\n $operator = '=';\n- $fieldname = \"UNIX_TIMESTAMP(DATE(FROM_UNIXTIME({$fieldname})))\";\n+ // Use validated fieldname only — never interpolate raw user input into SQL functions\n+ $fieldname = sprintf('UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(%s)))', $db->quoteIdentifier($fieldname));\n }\n```\n\nHappy to submit this as a PR against a private fork if that is the preferred workflow.", |
| "introduced": "0" | ||
| }, | ||
| { | ||
| "fixed": "2.3.6" | ||
| "fixed": "1.7.18" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "database_specific": { | ||
| "last_known_affected_version_range": "<= 2.3.5" | ||
| "last_known_affected_version_range": "<= 1.7.17" |
|
Hi there @kingjia90! A community member has suggested an improvement to your security advisory. If approved, this change will affect the global advisory listed at github.com/advisories. It will not affect the version listed in your project repository. This change will be reviewed by our Security Curation Team. If you have thoughts or feedback, please share them in a comment here! If this PR has already been closed, you can start a new community contribution for this advisory |
|
👋 This pull request has been marked as stale because it has been open with no activity. You can: comment on the issue or remove the stale label to hold stale off for a while, add the |
c0ec1dc
into
kingjia90/advisory-improvement-7899
|
Hi @kingjia90! Thank you so much for contributing to the GitHub Advisory Database. This database is free, open, and accessible to all, and it's people like you who make it great. Thanks for choosing to help others. We hope you send in more contributions in the future! |
Updates
Comments
a lower version with the security fix for LTS purposes got released after first publish