GHSA-gxmj-r5rf-ggwq: Malicious File Upload
Summary
elFinder provides uploadDeny and uploadAllow options in its connector configuration to restrict which MIME types may be uploaded. When uploadDeny includes text/x-php, direct upload of .php, .phtml, and .phar files is correctly blocked. However, the extract command (ZIP decompression) internally calls checkExtractItems(), which invokes mimetypeInternalDetect() directly without passing the result through mimeTypeNormalize(). Because phtml, phar, and similar PHP-executable extensions are absent from mime.types, they are not resolved to text/x-php at the detection stage, causing the MIME filter to be silently bypassed. An attacker who is permitted to upload ZIP archives can therefore extract PHP-executable files into the web-accessible files/ directory. If the server is configured to execute the affected extension (e.g., .phtml, .phar) as PHP — which is the case in common Apache and Nginx deployments — this results in Remote Code Execution.
---
Details
elFinder's MIME validation pipeline for direct uploads (upload command) is:
mimetype() └─ mimetypeInternalDetect() // stage 1: extension → MIME via mime.types └─ mimeTypeNormalize() // stage 2: apply staticMimeMap phtml: → text/x-php phar: → text/x-php php5: → text/x-php └─ allowPutMime() // blocked: text/x-php ∈ uploadDeny
The extract command (checkExtractItems() in elFinderVolumeDriver.class.php, line 7110) uses a shortened pipeline:
php // line 7110 — stage 2 (mimeTypeNormalize) is never called if ($chkMime && ($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && !$this->allowPutMime($mimeByName)) {
Because phtml and phar are not present in mime.types, mimetypeInternalDetect() returns a generic type (e.g., application/octet-stream) for these extensions. Without mimeTypeNormalize(), the staticMimeMap entries that would map phtml: → text/x-php are never applied, so allowPutMime() sees a non-blocked MIME and permits extraction.
Affected extensions confirmed: .phtml, .phar, .php5, .php3 Not bypassed: .php (present in mime.types, detected as text/x-php in stage 1)
---
PoC
Requirements: - elFinder 2.1.69 deployed under Apache/Nginx (PHP-FPM or modphp) - Connector configured with uploadDeny = ['text/x-php'] and uploadAllow including application/zip - files/ directory served under a public web path
Step 1 — Confirm direct upload is blocked Open elFinder in a browser and click the Upload button. Select hello.phtml (content: <?php phpinfo(); ?>). → Upload is rejected with: "Upload file hello.phtml: File type not allowed (text/x-php)" <img width="1061" height="408" alt="1" src="https://github.com/user-attachments/assets/22f833d9-7d2a-4197-85c2-3b0eb19ecfbc" />
Step 2 — Upload a ZIP containing the payload Create bypass.zip containing hello.phtml. Upload bypass.zip via the Upload button. → ZIP is accepted (MIME: application/zip ∈ uploadAllow). <img width="886" height="320" alt="2" src="https://github.com/user-attachments/assets/30c3498f-91f4-45cf-8c2b-cc806716e3a4" />
Step 3 — Extract the ZIP Right-click bypass.zip in the file list → Extract files. → hello.phtml appears in the file list without any error. → File is now present at {filesdir}/hello.phtml on the server. <img width="1123" height="792" alt="3" src="https://github.com/user-attachments/assets/49e7db8e-566c-4653-a57f-e80fb31ea61d" />
Step 4 — Execute the extracted PHP file
Navigate to: http://<target>/elFinder/files/hello.phtml → Apache processes the file as PHP and renders the full phpinfo() output, confirming Remote Code Execution.
--- <img width="1059" height="739" alt="4" src="https://github.com/user-attachments/assets/ad85fdf7-bdc7-4514-bdec-e45cfb2e2bd3" />
Impact
Any user with ZIP upload permission can bypass the uploadDeny MIME restriction, place PHP-executable files in a web-accessible directory, and achieve Remote Code Execution on the server.
Concrete impact: - Arbitrary PHP code execution on the web server - Full server environment disclosure via phpinfo() (paths, PHP version, loaded modules, environment variables) - Potential access to server filesystem, database credentials, and internal network services - Complete compromise of the web application if an attacker substitutes phpinfo() with a web shell (e.g., <?php system($GET['cmd']); ?>)
Extensions confirmed executable on Apache (default config): phtml, phar, php5, php3
Recommended fix:
Apply mimeTypeNormalize() inside checkExtractItems() so that the full MIME pipeline is used consistently:
php // elFinderVolumeDriver.class.php, line 7110 // Before (vulnerable): $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)
// After (fixed): $mimeByName = $this->mimeTypeNormalize( elFinderVolumeDriver::mimetypeInternalDetect($name), $name, pathinfo($name, PATHINFOEXTENSION) )
---
---
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
composer/Studio-42/elFinderto a version that resolves this vulnerability.Fixed in 2.1.70 - Upgrade
Upgrade
elFinder 2.1.69to a version that resolves this vulnerability.Fixed in 2.1.69 - Configuration
Verify the elFinder connector is configured so that uploadDeny blocks 'text/x-php' while uploadAllow permits 'application/zip' (as described: ZIP upload is allowed but PHP MIME types should be denied).
elFinder connector (uploadDeny/uploadAllow) uploadDeny / uploadAllow = uploadDeny includes 'text/x-php' and uploadAllow includes 'application/zip' - Configuration
Apply the code fix described: in elFinderVolumeDriver.class.php (checkExtractItems(), referenced around line 7110), call mimeTypeNormalize() so the full MIME pipeline is used consistently for extracted items; specifically avoid the shortened pipeline where stage 2 (mimeTypeNormalize) is never called during extraction.
elFinderVolumeDriver.class.php (checkExtractItems) mimetypeInternalDetect() pipeline = Ensure mimeTypeNormalize() is applied during checkExtractItems() extract path
Event History
Frequently Asked Questions
What conditions are required for exploitation?
An unauthenticated attacker needs to be permitted to upload a ZIP archive and invoke extraction. The extracted PHP-executable file must land in the web-accessible files/ directory, and the web server must execute its extension as PHP.
Does blocking text/x-php prevent this issue?
It blocks direct uploads of .php, .phtml, and .phar files, but it does not prevent the affected ZIP extraction path from bypassing the MIME filter. Extensions such as .phtml and .phar may not be normalized to text/x-php during extraction.
Which deployments are most exposed?
Deployments that allow ZIP uploads and extraction into a web-accessible files/ directory are exposed if their server executes extracted extensions such as .phtml or .phar as PHP. The advisory identifies common Apache and Nginx configurations as examples where those extensions may be executable.
What can be done while patching is unavailable?
Prevent untrusted users from uploading ZIP archives or using the extraction function. Configure the web server so files in the files/ directory, including .phtml and .phar files, cannot be executed as PHP.
How can an administrator assess whether their configuration is affected?
Check whether ZIP upload and extraction are available to untrusted users, whether extraction writes into the web-accessible files/ directory, and whether the server executes .phtml, .phar, or similar extensions as PHP. A configuration that only relies on uploadDeny containing text/x-php does not protect the affected extraction path.