VYPR
Moderate severityNVD Advisory· Published Nov 25, 2025· Updated Nov 25, 2025

CVE-2025-64049

CVE-2025-64049

Description

A stored cross-site scripting (XSS) vulnerability in the module management component in REDAXO CMS 5.20.0 allows remote users to inject arbitrary web script or HTML via the Output code field in modules. The payload is executed when a user views or edits an article by adding slice that uses the compromised module.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Stored XSS in REDAXO CMS 5.20.0 allows remote attackers to inject arbitrary script via the module Output code field.

Vulnerability

Analysis

A stored cross-site scripting (XSS) vulnerability was discovered in the module management component of REDAXO CMS version 5.20.0. The flaw resides in the module editor, where the 'Output code' field does not properly sanitize user-supplied content before storing it in the database. When an administrator creates or edits a module, arbitrary HTML or JavaScript embedded in the Output code field is persisted and later executed without escaping [1][4]. This root cause is similar to a previously addressed escaping issue in the media pool component, which was fixed by wrapping user-controlled strings with rex_escape() [2].

Exploitation

An attacker with access to the module management interface can craft a malicious payload and insert it into the Output code field. The attack requires the attacker to have the necessary permissions to create or modify modules, typically granted to backend administrators. The stored payload is then triggered when a user (including other administrators) views or edits an article and adds a slice that uses the compromised module [1][4]. The payload executes in the context of the REDAXO backend session, as demonstrated by a proof-of-concept where an alert dialog appears upon saving the slice [4].

Impact

Successful exploitation leads to persistent execution of arbitrary JavaScript in the backend of the CMS. An attacker can steal session cookies of authenticated administrators, effectively seizing full control of the application. The malicious script persists in the database and executes repeatedly each time the affected module is used, amplifying the damage [4].

Mitigation

REDAXO has addressed the underlying issue in its development branch by implementing proper escaping of user-supplied data in output [2]. Administrators of REDAXO 5.20.0 should apply the official patch or upgrade to a patched version as soon as it becomes available. As a workaround, manual escaping of the Output code field may be enforced by reviewing module inputs [4].

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
redaxo/sourcePackagist
< 5.20.15.20.1

Affected products

2
  • REDAXO/REDAXO CMSdescription
  • Redaxo/Redaxollm-fuzzy
    Range: = 5.20.0

Patches

1
58929062312c

Medienpool: Fehlendes Escaping ergänzt (#6375)

https://github.com/redaxo/redaxoGregor HarlanNov 25, 2025via ghsa
3 files changed · +11 11
  • redaxo/src/addons/mediapool/lib/service_media.php+5 5 modified
    @@ -39,17 +39,17 @@ public static function addMedia(array $data, bool $doSubindexing = true, array $
             }
     
             if (!rex_mediapool::isAllowedExtension($data['file']['name'], $allowedExtensions)) {
    -            $warning = rex_i18n::msg('pool_file_mediatype_not_allowed') . ' <code>' . rex_file::extension($data['file']['name']) . '</code>';
    +            $warning = rex_i18n::msg('pool_file_mediatype_not_allowed') . ' <code>' . rex_escape(rex_file::extension($data['file']['name'])) . '</code>';
                 $allowedExtensions = rex_mediapool::getAllowedExtensions($allowedExtensions);
                 $warning .= count($allowedExtensions) > 0
    -                    ? '<br />' . rex_i18n::msg('pool_file_allowed_mediatypes') . ' <code>' . rtrim(implode('</code>, <code>', $allowedExtensions), ', ') . '</code>'
    -                    : '<br />' . rex_i18n::msg('pool_file_banned_mediatypes') . ' <code>' . rtrim(implode('</code>, <code>', rex_mediapool::getBlockedExtensions()), ', ') . '</code>';
    +                    ? '<br />' . rex_i18n::msg('pool_file_allowed_mediatypes') . ' <code>' . implode('</code>, <code>', rex_escape($allowedExtensions)) . '</code>'
    +                    : '<br />' . rex_i18n::msg('pool_file_banned_mediatypes') . ' <code>' . implode('</code>, <code>', rex_escape(rex_mediapool::getBlockedExtensions())) . '</code>';
     
                 throw new rex_api_exception($warning);
             }
     
             if (!rex_mediapool::isAllowedMimeType($data['file']['path'], $data['file']['name'])) {
    -            $warning = rex_i18n::msg('pool_file_mediatype_not_allowed') . ' <code>' . rex_file::extension($data['file']['name']) . '</code> (<code>' . rex_file::mimeType($data['file']['path']) . '</code>)';
    +            $warning = rex_i18n::msg('pool_file_mediatype_not_allowed') . ' <code>' . rex_escape(rex_file::extension($data['file']['name'])) . '</code> (<code>' . rex_escape(rex_file::mimeType($data['file']['path'])) . '</code>)';
                 throw new rex_api_exception($warning);
             }
     
    @@ -202,7 +202,7 @@ public static function updateMedia(string $filename, array $data): array
                     || in_array($extensionNew, ['jpg', 'jpeg']) && in_array($extensionOld, ['jpg', 'jpeg'])
                 ) {
                     if (!rex_mediapool::isAllowedMimeType($srcFile, $dstFile)) {
    -                    $warning = rex_i18n::msg('pool_file_mediatype_not_allowed') . ' <code>' . $extensionNew . '</code> (<code>' . ($filetype ?? 'unknown mime type') . '</code>)';
    +                    $warning = rex_i18n::msg('pool_file_mediatype_not_allowed') . ' <code>' . rex_escape($extensionNew) . '</code> (<code>' . rex_escape($filetype ?? 'unknown mime type') . '</code>)';
                         throw new rex_api_exception($warning);
                     }
                     if (!rex_file::move($srcFile, $dstFile)) {
    
  • redaxo/src/addons/mediapool/pages/media.list.php+1 1 modified
    @@ -122,7 +122,7 @@
     }
     
     if (!empty($argUrl['args']['types'])) {
    -    echo rex_view::info(rex_i18n::msg('pool_file_filter') . ' <code>' . $argUrl['args']['types'] . '</code>');
    +    echo rex_view::info(rex_i18n::msg('pool_file_filter') . ' <code>' . rex_escape($argUrl['args']['types']) . '</code>');
     }
     
     $addon = rex_addon::require('mediapool');
    
  • .tools/psalm/baseline.xml+5 5 modified
    @@ -1287,19 +1287,19 @@
       </file>
       <file src="redaxo/src/addons/mediapool/lib/service_media.php">
         <MixedArgumentTypeCoercion>
    -      <code><![CDATA[$allowedExtensions]]></code>
           <code><![CDATA[$queryParams]]></code>
    -      <code><![CDATA[rex_mediapool::getBlockedExtensions()]]></code>
    +      <code><![CDATA[rex_escape($allowedExtensions)]]></code>
    +      <code><![CDATA[rex_escape(rex_mediapool::getBlockedExtensions())]]></code>
         </MixedArgumentTypeCoercion>
    +    <NullOperand>
    +      <code><![CDATA[rex_escape(rex_file::mimeType($data['file']['path']))]]></code>
    +    </NullOperand>
         <PossiblyFalseArgument>
           <code><![CDATA[$content]]></code>
         </PossiblyFalseArgument>
         <PossiblyInvalidOperand>
           <code><![CDATA[$uses]]></code>
         </PossiblyInvalidOperand>
    -    <PossiblyNullOperand>
    -      <code><![CDATA[rex_file::mimeType($data['file']['path'])]]></code>
    -    </PossiblyNullOperand>
       </file>
       <file src="redaxo/src/addons/mediapool/lib/service_media_category.php">
         <MixedArgument>
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.