CVE-2026-45008: phpMyFAQ: Path Traversal in Client::deleteClientFolder enables arbitrary directory deletion by non-super-admin admins
Summary
Client::deleteClientFolder() in phpmyfaq/src/phpMyFAQ/Instance/Client.php:583 takes a URL from the caller, strips the https:// prefix, and passes the remainder to Filesystem::deleteDirectory() relative to the multisite clientFolder. No path-traversal validation runs. An admin with the INSTANCEDELETE permission (a role short of SUPERADMIN) submits https://../../../<path> as the client URL and the server recursively deletes arbitrary directories under the web user's rights. Same pattern and reachability as GHSA-38m8-xrfj-v38x, which the project accepted at High severity three weeks earlier.
Details
phpmyfaq/src/phpMyFAQ/Instance/Client.php:583-591:
php public function deleteClientFolder(string $sourceUrl): bool { if (!$this->isMultiSiteWriteable()) { return false; }
$sourcePath = strreplace(search: 'https://', replace: '', subject: $sourceUrl); return $this->filesystem->deleteDirectory($this->clientFolder . $sourcePath); }
strreplace strips the scheme but does nothing about ../ segments. The concatenation $this->clientFolder . $sourcePath directly feeds the filesystem call, which traverses above clientFolder without complaint.
Callers feed the URL from the HTTP request body:
phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/InstanceController.php:184:
php if (1 !== $instanceId && $client->deleteClientFolder($clientData->url) && $client->delete($instanceId)) {
$clientData->url comes from jsondecode($request->getContent()). The route is admin.api.instance.delete, gated by INSTANCEDELETE. The controller does not validate the URL against a scheme list or canonicalize the path before handing it to deleteClientFolder().
InstanceController.php:144 (edit path) and Controller/Administration/InstanceController.php:151 (form path) both reach the same sink through different entry points.
Precedent
GHSA-38m8-xrfj-v38x (2026-03-31) disclosed the identical bug class in MediaBrowserController::index(): an admin-gated API endpoint concatenates a user-supplied filename to a base directory without traversal validation. phpMyFAQ accepted that report at High severity. The present finding is the same root cause in a different controller; the project's INSTANCEADD / INSTANCEDELETE permission is a granular admin right, not SUPERADMIN, so a lower-tier admin can reach the sink.
Proof of Concept
Prerequisites: a phpMyFAQ 4.2.x instance with the multisite subsystem bootstrapped (there must be a non-primary instance present for the delete controller branch to fire). Alice is an admin with INSTANCEADD and INSTANCEDELETE rights, no SUPERADMIN flag.
Step 1: Alice authenticates and retrieves the CSRF token for the instance admin page.
Step 2: Alice creates an instance whose url encodes a traversal payload. The create path at InstanceController.php:144 already concatenates to the clientFolder through the same deleteClientFolder('https://' . $hostname) call:
bash curl -sS -b "$ALICECOOKIE" -X POST "$BASE/admin/api/instance" \ -H "Content-Type: application/json" -H "x-csrf-token: $CSRF" \ -d '{"url":"https://../../../tmp/pmf-poc/","instance":"poc","comment":"poc","email":"a@b","admin":"alice","password":"poc1234!"}'
Step 3: Alice deletes the instance. The request body names the instance id to delete; the controller hands clientData->url directly to deleteClientFolder:
bash curl -sS -b "$ALICECOOKIE" -X POST "$BASE/admin/api/instance/2" \ -H "Content-Type: application/json" -H "x-csrf-token: $CSRF" \ -d '{"url":"https://../../../tmp/pmf-poc/"}'
The server computes $sourcePath = '../../../tmp/pmf-poc/', concatenates to <clientFolder>/, and recursively deletes the resulting path.
Live verification was not attempted against the test instance because the INSTANCEDELETE path requires the multisite/ subsystem to be bootstrapped with at least one non-primary instance; see InstanceController.php:184. The code path is unambiguous and the precedent GHSA confirmed the same admin gating was considered in-scope.
Impact
Any phpMyFAQ admin holding INSTANCEADD + INSTANCEDELETE but not SUPERADMIN can delete arbitrary directories writable by the PHP process. Outcomes:
- Destroy other tenants' data on a shared multisite deployment by traversing above the clientFolder into peer directories. - Delete phpMyFAQ's own content/, config/, or cache directories and lock the install out. - On a hosted deployment, overwrite or delete files anywhere under the web user's reach, including customer uploads outside phpMyFAQ.
phpMyFAQ's permission model gives INSTANCEADD / INSTANCEDELETE as a role that a hosting operator may delegate to a subordinate admin without granting SUPERADMIN. That delegation is now a direct path-traversal-delete primitive.
Recommended Fix
Canonicalize and validate the URL before forming the filesystem path.
phpmyfaq/src/phpMyFAQ/Instance/Client.php:583:
php public function deleteClientFolder(string $sourceUrl): bool { if (!$this->isMultiSiteWriteable()) { return false; }
$parsed = parseurl($sourceUrl); if (!isarray($parsed) || !isset($parsed['host']) || ($parsed['scheme'] ?? '') !== 'https') { return false; }
$host = $parsed['host']; if (!pregmatch('/^[a-z0-9][a-z0-9.-]$/i', $host)) { return false; }
$target = realpath($this->clientFolder . $host); $root = realpath($this->clientFolder); if ($target === false || $root === false || !strstartswith($target, $root . DIRECTORYSEPARATOR)) { return false; }
return $this->filesystem->deleteDirectory($target); }
parseurl rejects malformed inputs, the regex pins the host to valid DNS characters (no /, no ..), and the realpath check ensures the resolved target lives under clientFolder. Apply the same canonicalization at the controller layer (InstanceController::add, ::update, ::delete) so the URL is validated before every call that touches the filesystem.
--- Found by aisafe.io
Other sources
phpMyFAQ before 4.1.2 contains a path traversal vulnerability in Client::deleteClientFolder that allows admins with INSTANCEDELETE permission to delete arbitrary directories. Attackers can submit traversal sequences like https://../../../<path> in the client URL parameter to recursively delete directories outside the intended clientFolder scope.
— MITRE
phpMyFAQ: Path Traversal in Client::deleteClientFolder enables arbitrary directory deletion by non-super-admin admins
— Packagist
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
composer/phpmyfaq/phpmyfaqto a version that resolves this vulnerability.Fixed in 4.1.2 - Upgrade
Upgrade
composer/thorsten/phpmyfaqto a version that resolves this vulnerability.Fixed in 4.1.2 - Upgrade
Upgrade
phpMyFAQto a version that resolves this vulnerability.Fixed in 4.1.2 - Compensating control
Canonicalize and validate the client URL before forming filesystem paths or calling deleteClientFolder(). Apply this at InstanceController::add, ::update, and ::delete: parse the URL, require a valid host matching /^[a-z0-9][a-z0-9.-]*$/i, reject malformed or unsupported schemes, resolve the target with realpath, and require the resolved target to remain under clientFolder.
Event History
Frequently Asked Questions
What is the severity of CVE-2026-45008?
CVE-2026-45008 is classified as a critical vulnerability due to its potential to allow unauthorized directory deletion.
How do I fix CVE-2026-45008?
To resolve CVE-2026-45008, upgrade to phpMyFAQ version 4.1.2 or later.
Who is affected by CVE-2026-45008?
CVE-2026-45008 affects phpMyFAQ versions prior to 4.1.2 that allow admins with INSTANCE_DELETE permission to exploit the vulnerability.
What kind of attack is possible with CVE-2026-45008?
CVE-2026-45008 allows attackers to execute path traversal attacks to delete arbitrary directories.
What components of phpMyFAQ are vulnerable in CVE-2026-45008?
The vulnerability exists specifically in the Client::deleteClientFolder function of phpMyFAQ.