PatchClock

PHP security digest · 24 Sep 2026

REDAXO: Unwhitelisted ORDER BY Column in rex_list Allows Authenticated Column Enumeration

Medium CVE-2026-62998

Package
redaxo/source
Fixed in
5.21.2
Published
Thu 24 Sep 2026, 15:57 LisbonUpdated Mon 26 Oct 2026, 01:26 Lisbon
Installs on Packagist
24K

In plain words

An authenticated REDAXO backend user, including a non-admin, can discover internal database column names through error messages and sort lists by sensitive columns that are not displayed. This affects lists using rex_list, including the users list. It does not allow arbitrary SQL execution, because column names are escaped.

Affected versions

PackageAffectedFixed in
redaxo/source<= 5.21.15.21.2

Am I affected?

Check composer.lock for redaxo/source at version 5.21.1 or earlier; 5.21.2 is fixed. Check whether backend users can access lists using rex_list. The description identifies MySQL error messages and the users list querying rex_user, which contains sensitive columns not selected for display.

What to do

Check which version you have, then update to a fixed release for your branch (5.21.2):

composer show redaxo/source | grep versions
composer update redaxo/source

If your version constraint in composer.json keeps you below a fixed release, raise it first. Whether your configuration is affected, and the exact version ranges, are in the advisory itself.

If you cannot update yet

Validate the sort request parameter against the whitelist of columns registered with setColumnSortable() before using it in the query.

The advisory's description

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;
}

Weakness: CWE-20 Improper Input Validation; CWE-200 Exposure of Sensitive Information to an Unauthorized Actor. Reported by de3erve-hunter.

The advisory

From OSV.dev, which gathers the GitHub Advisory Database, FriendsOfPHP and Drupal security advisories; the summary is theirs. The description, affected versions, weakness and credits are from the GitHub Advisory Database (CC BY 4.0). "In plain words", "Am I affected?" and "If you cannot update yet" are based on that advisory, and checked to name no version it does not.