{"id":"CVE-2026-81891","aliases":["GHSA-gxmj-r5rf-ggwq"],"url":"https://o3.security/vulnerability/CVE-2026-81891","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->allowPutMime($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: `<?php phpinfo(); ?>`).  \n→ Upload is rejected with: *\"Upload file hello.phtml: File type not allowed (text/x-php)\"*\n<img width=\"1061\" height=\"408\" alt=\"1\" src=\"https://github.com/user-attachments/assets/22f833d9-7d2a-4197-85c2-3b0eb19ecfbc\" />\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<img width=\"886\" height=\"320\" alt=\"2\" src=\"https://github.com/user-attachments/assets/30c3498f-91f4-45cf-8c2b-cc806716e3a4\" />\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<img width=\"1123\" height=\"792\" alt=\"3\" src=\"https://github.com/user-attachments/assets/49e7db8e-566c-4653-a57f-e80fb31ea61d\" />\n\n\n**Step 4 — Execute the extracted PHP file**\n\nNavigate to:\n```\nhttp://<target>/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<img width=\"1059\" height=\"739\" alt=\"4\" src=\"https://github.com/user-attachments/assets/ad85fdf7-bdc7-4514-bdec-e45cfb2e2bd3\" />\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., `<?php system($_GET['cmd']); ?>`)\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->mimeTypeNormalize(\n    elFinderVolumeDriver::mimetypeInternalDetect($name),\n    $name,\n    pathinfo($name, PATHINFO_EXTENSION)\n)\n```\n\n---\n\n---","published":"2026-08-31T20:33:22.283Z","modified":"2026-09-11T03:30:49.907071709Z","cvss":{"score":8.1,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},"epss":{"score":0.00528,"percentile":0.4338,"asOf":"2026-09-16"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Packagist","name":"Studio-42/elFinder","fixedVersion":"2.1.70"}],"fix":{"url":"https://github.com/Studio-42/elFinder/commit/191372c1bbebbd36fb55af79a84b9984861390ff","label":"Studio-42/elFinder@191372c"},"references":[{"type":"WEB","url":"https://github.com/Studio-42/elFinder/releases/tag/2.1.70"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/81xxx/CVE-2026-81891.json"},{"type":"ADVISORY","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":"FIX","url":"https://github.com/Studio-42/elFinder/commit/191372c1bbebbd36fb55af79a84b9984861390ff"},{"type":"FIX","url":"https://github.com/Studio-42/elFinder/commit/dd73e702820c146a192969800ee674ecdb208365"},{"type":"PACKAGE","url":"https://github.com/Studio-42/elFinder"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-11T03:30:49.907071709Z"}}