{"id":"GHSA-4f5f-j737-pm58","summary":"REDAXO: Unwhitelisted ORDER BY Column in rex_list Allows Authenticated Column Enumeration","details":"### Summary\nThe `rex_list` component reads the SQL sort column directly from the `sort` GET parameter without validating it against the set of columns declared sortable via `setColumnSortable()`. Although the value is wrapped in backticks via `escapeIdentifier()` (preventing classical SQL injection), this still allows any authenticated backend user to ORDER BY any column in the query's FROM tables, including unselected sensitive columns such as `password` from the `rex_user` table, and perform error-based column enumeration.\n\n### Details\n**File:** `redaxo/src/core/lib/list.php:976-982` — `getSortColumn()` returns the raw request parameter without whitelist check:\n```php\npublic function getSortColumn($default = null)\n{\n    if (rex_request('list', 'string') == $this-\u003egetName()) {\n        return rex_request('sort', 'string', $default);  // NO validation against sortable columns\n    }\n    return $default;\n}\n```\n\n**File:** `redaxo/src/core/lib/list.php:899-911` — `prepareQuery()` uses it directly in the ORDER BY clause:\n```php\nprotected function prepareQuery($query, array $defaultSort = [])\n{\n    $sortColumn = $this-\u003egetSortColumn();\n    if ('' != $sortColumn) {\n        $sql = rex_sql::factory($this-\u003edb);\n        $sortColumn = $sql-\u003eescapeIdentifier($sortColumn);  // backtick-wraps, but no whitelist\n        if ($defaultSort || false === stripos($query, ' ORDER BY ')) {\n            $query .= ' ORDER BY ' . $sortColumn . ' ' . $sortType;\n        }\n    }\n```\n\nThe users list queries `rex_user` which contains `password`, `previous_passwords`, `password_change_required` — not in the SELECT. Specifying a non-existent column name produces a MySQL `Unknown column` exception whose message is propagated to the user, confirming or denying column existence.\n\n### PoC\n**Column enumeration (error-based):**\n```\nGET /redaxo/index.php?page=users&list=\u003clist_name\u003e&sort=nonexistent_col&sorttype=asc\n```\nResponse will contain: `Unknown column 'nonexistent_col' in 'order clause'`\n\n**Sort by password hash (data ordering leak):**\n```\nGET /redaxo/index.php?page=users&list=\u003clist_name\u003e&sort=password&sorttype=asc\n```\nUsers are silently reordered by their Argon2 password hash.\n\n### Impact\nAuthenticated backend users (non-admin) can enumerate database column names of internal tables via error messages and manipulate query ordering to include sensitive unselected columns. While this does not allow arbitrary SQL execution due to backtick escaping, it constitutes an information disclosure vulnerability enabling targeted further attacks.\n\n### Fix\nValidate the `sort` request parameter against the whitelist of columns registered with `setColumnSortable()` before use in the query:\n```php\npublic function getSortColumn($default = null)\n{\n    if (rex_request('list', 'string') == $this-\u003egetName()) {\n        $requested = rex_request('sort', 'string', $default);\n        if ($requested !== null && $this-\u003ehasColumnOption($requested, REX_LIST_OPT_SORT)) {\n            return $requested;\n        }\n    }\n    return $default;\n}\n```","aliases":["CVE-2026-62998"],"modified":"2026-09-24T15:00:04.704179552Z","published":"2026-09-24T14:57:07Z","database_specific":{"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-09-24T14:57:07Z","nvd_published_at":"2026-09-23T15:17:15Z","cwe_ids":["CWE-20","CWE-200"]},"references":[{"type":"WEB","url":"https://github.com/redaxo/core/security/advisories/GHSA-4f5f-j737-pm58"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-62998"},{"type":"WEB","url":"https://github.com/redaxo/core/pull/6580"},{"type":"WEB","url":"https://github.com/redaxo/core/commit/c44ba5206a28427984100c994010f2aaa6703efd"},{"type":"PACKAGE","url":"https://github.com/redaxo/core"},{"type":"WEB","url":"https://github.com/redaxo/core/releases/tag/5.21.2"}],"affected":[{"package":{"name":"redaxo/source","ecosystem":"Packagist","purl":"pkg:composer/redaxo/source"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.21.2"}]}],"versions":["5.10.0","5.10.0-beta1","5.10.0-beta2","5.10.1","5.11.0","5.11.0-beta1","5.11.1","5.11.2","5.12.0","5.12.0-beta1","5.12.0-beta2","5.12.0-beta3","5.12.1","5.13.0","5.13.0-beta1","5.13.0-beta2","5.13.1","5.13.2","5.13.3","5.14.0","5.14.0-beta1","5.14.0-beta2","5.14.1","5.14.2","5.14.3","5.15.0","5.15.0-beta1","5.15.1","5.16.0","5.16.0-beta1","5.16.1","5.17.0","5.17.1","5.18.0","5.18.1","5.18.2","5.18.3","5.19.0","5.20.0","5.20.1","5.20.2","5.21.0","5.21.0-beta1","5.21.1"],"database_specific":{"last_known_affected_version_range":"\u003c= 5.21.1","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-4f5f-j737-pm58/GHSA-4f5f-j737-pm58.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N"}]}