GHSA-4F5F-J737-PM58

Vulnerability from github – Published: 2026-09-24 14:57 – Updated: 2026-09-24 14:57
VLAI
Summary
REDAXO: Unwhitelisted ORDER BY Column in rex_list Allows Authenticated Column Enumeration
Details

Summary

The 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.

Details

File: redaxo/src/core/lib/list.php:976-982 — getSortColumn() returns the raw request parameter without whitelist check:

public function getSortColumn($default = null)
{
    if (rex_request('list', 'string') == $this->getName()) {
        return rex_request('sort', 'string', $default);  // NO validation against sortable columns
    }
    return $default;
}

File: redaxo/src/core/lib/list.php:899-911 — prepareQuery() uses it directly in the ORDER BY clause:

protected function prepareQuery($query, array $defaultSort = [])
{
    $sortColumn = $this->getSortColumn();
    if ('' != $sortColumn) {
        $sql = rex_sql::factory($this->db);
        $sortColumn = $sql->escapeIdentifier($sortColumn);  // backtick-wraps, but no whitelist
        if ($defaultSort || false === stripos($query, ' ORDER BY ')) {
            $query .= ' ORDER BY ' . $sortColumn . ' ' . $sortType;
        }
    }

The 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.

PoC

Column enumeration (error-based):

GET /redaxo/index.php?page=users&list=<list_name>&sort=nonexistent_col&sorttype=asc

Response will contain: Unknown column 'nonexistent_col' in 'order clause'

Sort by password hash (data ordering leak):

GET /redaxo/index.php?page=users&list=<list_name>&sort=password&sorttype=asc

Users are silently reordered by their Argon2 password hash.

Impact

Authenticated 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.

Fix

Validate the sort request parameter against the whitelist of columns registered with setColumnSortable() before use in the query:

public function getSortColumn($default = null)
{
    if (rex_request('list', 'string') == $this->getName()) {
        $requested = rex_request('sort', 'string', $default);
        if ($requested !== null && $this->hasColumnOption($requested, REX_LIST_OPT_SORT)) {
            return $requested;
        }
    }
    return $default;
}
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 5.21.1"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "redaxo/source"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "5.21.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-62998"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-20",
      "CWE-200"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-24T14:57:07Z",
    "nvd_published_at": "2026-09-23T15:17:15Z",
    "severity": "MODERATE"
  },
  "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\u0027s 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` \u2014 `getSortColumn()` returns the raw request parameter without whitelist check:\n```php\npublic function getSortColumn($default = null)\n{\n    if (rex_request(\u0027list\u0027, \u0027string\u0027) == $this-\u003egetName()) {\n        return rex_request(\u0027sort\u0027, \u0027string\u0027, $default);  // NO validation against sortable columns\n    }\n    return $default;\n}\n```\n\n**File:** `redaxo/src/core/lib/list.php:899-911` \u2014 `prepareQuery()` uses it directly in the ORDER BY clause:\n```php\nprotected function prepareQuery($query, array $defaultSort = [])\n{\n    $sortColumn = $this-\u003egetSortColumn();\n    if (\u0027\u0027 != $sortColumn) {\n        $sql = rex_sql::factory($this-\u003edb);\n        $sortColumn = $sql-\u003eescapeIdentifier($sortColumn);  // backtick-wraps, but no whitelist\n        if ($defaultSort || false === stripos($query, \u0027 ORDER BY \u0027)) {\n            $query .= \u0027 ORDER BY \u0027 . $sortColumn . \u0027 \u0027 . $sortType;\n        }\n    }\n```\n\nThe users list queries `rex_user` which contains `password`, `previous_passwords`, `password_change_required` \u2014 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\u0026list=\u003clist_name\u003e\u0026sort=nonexistent_col\u0026sorttype=asc\n```\nResponse will contain: `Unknown column \u0027nonexistent_col\u0027 in \u0027order clause\u0027`\n\n**Sort by password hash (data ordering leak):**\n```\nGET /redaxo/index.php?page=users\u0026list=\u003clist_name\u003e\u0026sort=password\u0026sorttype=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(\u0027list\u0027, \u0027string\u0027) == $this-\u003egetName()) {\n        $requested = rex_request(\u0027sort\u0027, \u0027string\u0027, $default);\n        if ($requested !== null \u0026\u0026 $this-\u003ehasColumnOption($requested, REX_LIST_OPT_SORT)) {\n            return $requested;\n        }\n    }\n    return $default;\n}\n```",
  "id": "GHSA-4f5f-j737-pm58",
  "modified": "2026-09-24T14:57:07Z",
  "published": "2026-09-24T14:57:07Z",
  "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"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "REDAXO: Unwhitelisted ORDER BY Column in rex_list Allows Authenticated Column Enumeration"
}



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…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…