CVE-2026-49972 Overview
CVE-2026-49972 is a file upload vulnerability in the Laravel-Mediable package before version 7.0.0. Unauthenticated attackers can achieve remote code execution by uploading a file with an embedded PHP extension disguised within a double extension, such as shell.php.jpg. The PATHINFO_FILENAME extraction preserves the inner .php extension in the base name. On misconfigured Apache or nginx servers that execute any filename containing .php as PHP, the stored file is interpreted as executable code. All MIME type, extension, and aggregate type validation checks pass due to the outer .jpg extension, giving attackers a reliable bypass path.
Critical Impact
Unauthenticated remote code execution on web servers running Laravel-Mediable below 7.0.0 with common Apache AddHandler or nginx fastcgi_split_path_info configurations.
Affected Products
- Laravel-Mediable package (plank/laravel-mediable) versions before 7.0.0
- Laravel applications integrating the vulnerable Mediable package
- Apache servers using AddHandler for PHP execution serving Laravel-Mediable uploads
Discovery Timeline
- 2026-07-13 - CVE-2026-49972 published to NVD
- 2026-07-15 - Last updated in NVD database
Technical Details for CVE-2026-49972
Vulnerability Analysis
The vulnerability is classified as an Unrestricted Upload of File with Dangerous Type [CWE-434]. Laravel-Mediable performs validation on the outer file extension, MIME type, and aggregate type. These checks correctly identify shell.php.jpg as an image because the trailing extension is .jpg. However, the destination filename retains the inner .php segment. When the resulting file is served by a web server that treats any path containing .php as PHP source, the server invokes the PHP interpreter on attacker-controlled content. The result is direct code execution in the context of the web server user.
Root Cause
The sanitizeFileName function in src/Helpers/File.php did not strip forbidden extensions when they appeared as nested segments of a destination filename. The forbidden_extensions list was only evaluated against the final extension. Filenames such as script.php.jpg were preserved verbatim after sanitization. The Laravel-Mediable configuration documentation did not warn integrators about Apache AddHandler x-httpd-php .php or nginx fastcgi_split_path_info patterns that match .php anywhere in the URI.
Attack Vector
An attacker with the ability to reach an upload endpoint submits a file named shell.php.jpg containing PHP code. Validation succeeds because the outer extension and detected MIME type resolve to an image. The file is written to a public storage location using its sanitized name. The attacker then requests the stored URL, and the misconfigured web server executes the embedded PHP payload.
// Security patch in src/Helpers/File.php (CVE-2026-49972 fix)
public static function sanitizeFileName(
string $file,
?string $language = null,
?array $forbiddenExtensions = null
): string {
$language = $language ?: App::currentLocale();
$forbiddenExtensions = $forbiddenExtensions ?? config('mediable.forbidden_file_extensions');
$pattern = "/[^a-zA-Z0-9\-_.%]+/";
if (!empty($forbiddenExtensions)) {
$forbiddenExtensions = array_map(
fn(string $ext) => preg_replace('[^a-z0-9]', '', strtolower($ext)),
$forbiddenExtensions
);
$forbiddenExtensions = implode('|', $forbiddenExtensions);
$pattern = "/[^a-zA-Z0-9\-_.%]+|\.(?=$forbiddenExtensions)/i";
}
}
// Source: https://github.com/plank/laravel-mediable/commit/49e3583bed13423611b3391f89e6b002571eed73
The patch adds a regex clause that replaces any period preceding a forbidden extension token. As a result, script.php.jpg is rewritten to script-php.jpg before the file lands on disk.
Detection Methods for CVE-2026-49972
Indicators of Compromise
- Uploaded files whose stored names contain multiple extensions where an inner segment matches php, phtml, phar, pl, py, jsp, asp, sh, cgi, or similar executable extensions.
- Web server access logs showing GET requests to media storage paths ending in .jpg, .png, or .pdf that returned text/html or application/x-httpd-php content types.
- New or modified PHP-interpretable files under Laravel storage/app/public or configured Mediable disks.
- Outbound connections from the web server process immediately following an upload request to /media or comparable endpoints.
Detection Strategies
- Audit filesystem contents of all Mediable-managed disks for filenames matching the regex \.(php|phtml|phar|pl|py|jsp)\.[a-z0-9]+$.
- Inspect Apache AddHandler and AddType directives and nginx location ~ \.php blocks for patterns that match .php anywhere in the URI rather than only as a terminal extension.
- Correlate application upload events with subsequent GET requests to the resulting URLs from the same client IP within short intervals.
Monitoring Recommendations
- Alert on any HTTP response from a media directory that carries a PHP content type or executes server-side.
- Enable file integrity monitoring on public upload directories and flag creation of files with more than one extension.
- Track process creation events where the web server user spawns shells, curl, wget, or interpreters shortly after an upload.
How to Mitigate CVE-2026-49972
Immediate Actions Required
- Upgrade plank/laravel-mediable to version 7.0.0 or later using composer update plank/laravel-mediable.
- Review web server configuration and ensure PHP execution is restricted to files whose final extension is .php, not any URI containing .php.
- Enumerate existing uploads for double-extension filenames and quarantine or rename any matches.
Patch Information
The fix is delivered in Laravel-Mediable 7.0.0 via commit 49e3583bed13423611b3391f89e6b002571eed73. The patch expands sanitizeFileName to accept a forbidden extensions list and rewrites nested forbidden extensions to hyphenated tokens. It also updates config/mediable.php documentation to describe the double-extension sanitization behavior. See the GitHub Release 7.0.0, the GitHub Commit Update, and the VulnCheck Advisory RCE for full technical details.
Workarounds
- Serve user-uploaded content from a dedicated domain or storage bucket that never invokes the PHP interpreter.
- Replace Apache AddHandler application/x-httpd-php .php with <FilesMatch "\.php$"> blocks that anchor the extension to the end of the filename.
- In nginx, change permissive location ~ \.php blocks to location ~ \.php$ and disable fastcgi_split_path_info for upload directories.
- Add server-side rewriting or middleware that rejects any uploaded filename containing more than one extension.
# nginx configuration example: restrict PHP execution to terminal .php extension
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
# Deny execution in upload directories
location ^~ /storage/ {
location ~ \.php {
deny all;
return 403;
}
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

