PatchClock

PHP security digest · 23 Sep 2026

REDAXO: Stored XSS in Mediapool Sync Page via Unescaped Filesystem Filenames

Medium CVE-2026-63002

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

In plain words

An attacker who can place files in REDAXO's media directory through admin-level backup restore or server access can cause stored JavaScript to run in another backend user's browser. This happens when a user with media[sync] permission opens the Mediapool sync page and it displays a malicious filename not yet registered in the media database. The flaw can enable session hijacking, credential theft and full CMS takeover, including through higher-privileged administrators.

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 an attacker could place files in /media/ through admin-level backup restore or server access without registering them in the media database, and whether backend users with media[sync] permission view the sync page. Both writable and non-writable filenames are rendered without escaping.

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

Apply rex_escape() to all filename variables before inserting them into HTML in redaxo/src/addons/mediapool/pages/sync.php, including the non-writable filename list.

The advisory's description

Summary

The mediapool sync page (sync.php) renders filenames from the /media filesystem directory directly into HTML without applying rex_escape() (i.e., htmlspecialchars). Any file placed in the media directory whose filename contains HTML metacharacters will execute JavaScript in the browser of any backend user who views the sync page.

Details

In redaxo/src/addons/mediapool/pages/sync.php, the variable $diffFiles is populated from actual filesystem filenames (files in /media/ not yet registered in the database). These filenames are then rendered without escaping:

File: redaxo/src/addons/mediapool/pages/sync.php:119-120

foreach ($diffFiles as $file) {
    if (is_writable(rex_path::media($file))) {
        $e = [];
        $e['label'] = '<label>' . $file . '</label>';          // NO rex_escape!
        $e['field'] = '<input type="checkbox" name="sync_files[]" value="' . $file . '" />'; // NO rex_escape!
        $writable[] = $e;
    } else {
        $notWritable[] = $file;
    }
}

File: redaxo/src/addons/mediapool/pages/sync.php:170

$fragment->setVar('body', '<ul><li>' . implode('</li><li>', $notWritable) . '</li></ul>', false);
// $notWritable contains unescaped filenames

By contrast, all other filename displays in the codebase use rex_escape($fname) (e.g., media.detail.php:236, media.list.php). The sync page is accessible to any backend user with the media[sync] permission (not exclusively admins).

PoC

  1. Place a file named <img src=x onerror=alert(document.cookie)>.txt into the REDAXO /media/ directory (via backup restore or server access) without adding it to the media database.
  2. Log in as any backend user with media[sync] permission.
  3. Navigate to Mediapool → Sync.
  4. The XSS payload executes immediately, stealing the admin session cookie.

Impact

Stored XSS in the admin panel. An attacker who can place files in the media directory (via admin-level backup restore or server access) can achieve persistent XSS against all users who visit the sync page, including higher-privileged admins. This enables session hijacking, credential theft, and full CMS takeover.

Fix

Apply rex_escape() to all filename variables before inserting into HTML:

$e['label'] = '<label>' . rex_escape($file) . '</label>';
$e['field'] = '<input type="checkbox" name="sync_files[]" value="' . rex_escape($file) . '" />';
// ...
$fragment->setVar('body', '<ul><li>' . implode('</li><li>', array_map('rex_escape', $notWritable)) . '</li></ul>', false);

Weakness: CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'). 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.