{"id":"GHSA-gxmj-r5rf-ggwq","summary":"elFinder: ZIP extraction bypasses uploadDeny MIME filter allowing PHP file upload (RCE)","details":"### Summary\n\nelFinder 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.\n\n---\n\n### Details\n\nelFinder's MIME validation pipeline for **direct uploads** (`upload` command) is:\n\n```\nmimetype()\n  └─ mimetypeInternalDetect()   // stage 1: extension → MIME via mime.types\n  └─ mimeTypeNormalize()        // stage 2: apply staticMimeMap\n       phtml:* → text/x-php\n       phar:*  → text/x-php\n       php5:*  → text/x-php\n  └─ allowPutMime()             // blocked: text/x-php ∈ uploadDeny\n```\n\nThe `extract` command (`checkExtractItems()` in `elFinderVolumeDriver.class.php`, line 7110) uses a **shortened** pipeline:\n\n```php\n// line 7110 — stage 2 (mimeTypeNormalize) is never called\nif ($chkMime\n    && ($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name))\n    && !$this-\u003eallowPutMime($mimeByName)) {\n```\n\nBecause `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.\n\n**Affected extensions confirmed:** `.phtml`, `.phar`, `.php5`, `.php3`  \n**Not bypassed:** `.php` (present in `mime.types`, detected as `text/x-php` in stage 1)\n\n---\n\n### PoC\n\n**Requirements:**\n- elFinder 2.1.69 deployed under Apache/Nginx (PHP-FPM or mod_php)\n- Connector configured with `uploadDeny = ['text/x-php']` and `uploadAllow` including `application/zip`\n- `files/` directory served under a public web path\n\n**Step 1 — Confirm direct upload is blocked**\nOpen elFinder in a browser and click the **Upload** button.  \nSelect `hello.phtml` (content: `\u003c?php phpinfo(); ?\u003e`).  \n→ Upload is rejected with: *\"Upload file hello.phtml: File type not allowed (text/x-php)\"*\n\u003cimg width=\"1061\" height=\"408\" alt=\"1\" src=\"https://github.com/user-attachments/assets/22f833d9-7d2a-4197-85c2-3b0eb19ecfbc\" /\u003e\n\n\n**Step 2 — Upload a ZIP containing the payload**\nCreate `bypass.zip` containing `hello.phtml`.  \nUpload `bypass.zip` via the **Upload** button.  \n→ ZIP is accepted (MIME: `application/zip` ∈ `uploadAllow`).\n\u003cimg width=\"886\" height=\"320\" alt=\"2\" src=\"https://github.com/user-attachments/assets/30c3498f-91f4-45cf-8c2b-cc806716e3a4\" /\u003e\n\n\n**Step 3 — Extract the ZIP**\nRight-click `bypass.zip` in the file list → **Extract files**.  \n→ `hello.phtml` appears in the file list without any error.  \n→ File is now present at `{files_dir}/hello.phtml` on the server.\n\u003cimg width=\"1123\" height=\"792\" alt=\"3\" src=\"https://github.com/user-attachments/assets/49e7db8e-566c-4653-a57f-e80fb31ea61d\" /\u003e\n\n\n**Step 4 — Execute the extracted PHP file**\n\nNavigate to:\n```\nhttp://\u003ctarget\u003e/elFinder/files/hello.phtml\n```\n→ Apache processes the file as PHP and renders the full `phpinfo()` output, confirming Remote Code Execution.\n\n---\n\u003cimg width=\"1059\" height=\"739\" alt=\"4\" src=\"https://github.com/user-attachments/assets/ad85fdf7-bdc7-4514-bdec-e45cfb2e2bd3\" /\u003e\n\n\n\n### Impact\n\nAny 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.\n\n**Concrete impact:**\n- Arbitrary PHP code execution on the web server\n- Full server environment disclosure via `phpinfo()` (paths, PHP version, loaded modules, environment variables)\n- Potential access to server filesystem, database credentials, and internal network services\n- Complete compromise of the web application if an attacker substitutes `phpinfo()` with a web shell (e.g., `\u003c?php system($_GET['cmd']); ?\u003e`)\n\n**Extensions confirmed executable on Apache (default config):**\nphtml, phar, php5, php3\n\n**Recommended fix:**\n\nApply `mimeTypeNormalize()` inside `checkExtractItems()` so that the full MIME pipeline is used consistently:\n\n```php\n// elFinderVolumeDriver.class.php, line 7110\n// Before (vulnerable):\n$mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)\n\n// After (fixed):\n$mimeByName = $this-\u003emimeTypeNormalize(\n    elFinderVolumeDriver::mimetypeInternalDetect($name),\n    $name,\n    pathinfo($name, PATHINFO_EXTENSION)\n)\n```\n\n---\n\n---","aliases":["CVE-2026-81891"],"modified":"2026-09-02T20:30:04.228208839Z","published":"2026-09-02T14:37:54Z","database_specific":{"github_reviewed_at":"2026-09-02T14:37:54Z","nvd_published_at":"2026-08-31T21:17:52Z","cwe_ids":["CWE-434"],"severity":"HIGH","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/Studio-42/elFinder/security/advisories/GHSA-gxmj-r5rf-ggwq"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-81891"},{"type":"WEB","url":"https://github.com/Studio-42/elFinder/commit/191372c1bbebbd36fb55af79a84b9984861390ff"},{"type":"WEB","url":"https://github.com/Studio-42/elFinder/commit/dd73e702820c146a192969800ee674ecdb208365"},{"type":"PACKAGE","url":"https://github.com/Studio-42/elFinder"},{"type":"WEB","url":"https://github.com/Studio-42/elFinder/releases/tag/2.1.70"}],"affected":[{"package":{"name":"Studio-42/elFinder","ecosystem":"Packagist","purl":"pkg:composer/Studio-42/elFinder"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.1.70"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-gxmj-r5rf-ggwq/GHSA-gxmj-r5rf-ggwq.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"}]}