[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 |
Updates
Comments
a lower version with the security fix for LTS purposes got released after first publish