GHSA-W998-QMW9-MF4M
Vulnerability from github – Published: 2026-09-23 14:06 – Updated: 2026-09-23 14:06Summary
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
- Place a file named
<img src=x onerror=alert(document.cookie)>.txtinto the REDAXO/media/directory (via backup restore or server access) without adding it to the media database. - Log in as any backend user with
media[sync]permission. - Navigate to Mediapool → Sync.
- 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);
{
"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-63002"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-23T14:06:04Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\nThe 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.\n\n### Details\nIn `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:\n\n**File:** `redaxo/src/addons/mediapool/pages/sync.php:119-120`\n```php\nforeach ($diffFiles as $file) {\n if (is_writable(rex_path::media($file))) {\n $e = [];\n $e[\u0027label\u0027] = \u0027\u003clabel\u003e\u0027 . $file . \u0027\u003c/label\u003e\u0027; // NO rex_escape!\n $e[\u0027field\u0027] = \u0027\u003cinput type=\"checkbox\" name=\"sync_files[]\" value=\"\u0027 . $file . \u0027\" /\u003e\u0027; // NO rex_escape!\n $writable[] = $e;\n } else {\n $notWritable[] = $file;\n }\n}\n```\n\n**File:** `redaxo/src/addons/mediapool/pages/sync.php:170`\n```php\n$fragment-\u003esetVar(\u0027body\u0027, \u0027\u003cul\u003e\u003cli\u003e\u0027 . implode(\u0027\u003c/li\u003e\u003cli\u003e\u0027, $notWritable) . \u0027\u003c/li\u003e\u003c/ul\u003e\u0027, false);\n// $notWritable contains unescaped filenames\n```\n\nBy 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).\n\n### PoC\n1. Place a file named `\u003cimg src=x onerror=alert(document.cookie)\u003e.txt` into the REDAXO `/media/` directory (via backup restore or server access) without adding it to the media database.\n2. Log in as any backend user with `media[sync]` permission.\n3. Navigate to **Mediapool \u2192 Sync**.\n4. The XSS payload executes immediately, stealing the admin session cookie.\n\n### Impact\nStored 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.\n\n### Fix\nApply `rex_escape()` to all filename variables before inserting into HTML:\n```php\n$e[\u0027label\u0027] = \u0027\u003clabel\u003e\u0027 . rex_escape($file) . \u0027\u003c/label\u003e\u0027;\n$e[\u0027field\u0027] = \u0027\u003cinput type=\"checkbox\" name=\"sync_files[]\" value=\"\u0027 . rex_escape($file) . \u0027\" /\u003e\u0027;\n// ...\n$fragment-\u003esetVar(\u0027body\u0027, \u0027\u003cul\u003e\u003cli\u003e\u0027 . implode(\u0027\u003c/li\u003e\u003cli\u003e\u0027, array_map(\u0027rex_escape\u0027, $notWritable)) . \u0027\u003c/li\u003e\u003c/ul\u003e\u0027, false);\n```",
"id": "GHSA-w998-qmw9-mf4m",
"modified": "2026-09-23T14:06:04Z",
"published": "2026-09-23T14:06:04Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/redaxo/core/security/advisories/GHSA-w998-qmw9-mf4m"
},
{
"type": "WEB",
"url": "https://github.com/redaxo/core/pull/6581"
},
{
"type": "WEB",
"url": "https://github.com/redaxo/core/commit/2daaa3a30570bc76a82f63fd21fb8c9c2cd5dc7c"
},
{
"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:H/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "REDAXO: Stored XSS in Mediapool Sync Page via Unescaped Filesystem Filenames"
}
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.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
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.