# phpMyFAQ's two-factor authentication login bypasses the password factor

phpMyFAQ's public /check endpoint logs in any 2FA-enabled account from a TOTP code and user-id alone, never verifying the password.

- Payout: Research
- Severity: high
- Program: phpMyFAQ
- Found by: waseem-cve
- Disclosed: 2026-09-24
- CVE: CVE-2026-56737
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-8gpw-xvpf-hvx5
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-8gpw-xvpf-hvx5
- Tags: area:web domain:web impact:ato impact:privesc kind:research platform:ghsa sev:high tech:php technique:regression technique:source-review vuln:access-control vuln:auth-bypass vuln:mfa-bypass

**Target:** POST /check frontend authentication endpoint
**Root cause:** The public 2FA verification endpoint calls twoFactorSuccess() (full session login) after validating only a TOTP token for an attacker-chosen user-id, with no server-side state binding it to a prior password check.
**Steps:**
1. Identify a target account with 2FA enabled and its numeric user-id (1 = admin in default installs).
2. POST to `https://target/check` with `user-id=1` and a 6-digit `token` guess, no password.
3. Iterate the token space across TOTP windows; no lockout on /check.
4. On success the response redirects to `./` and the session cookie in jar.txt is authenticated.
**How found:** Code review of the frontend AuthenticationController compared against the admin flow, which correctly sets a `2fa_pending_user_id` session value only after password validation — showing the frontend omission is a regression.
**Exploitation:** Unauthenticated attacker brute-forces the 6-digit TOTP space against /check for a chosen user-id; a correct guess yields a fully logged-in session without ever supplying the password. If the attacker already has the victim's TOTP device, a single request authenticates.
**Escalation:** Absent lockout plus default-disabled login throttle makes the 6-digit code brute-forceable, turning 2FA into the sole and guessable barrier; admin accounts (user-id 1) give full takeover.
**Insight:** When a 2FA step exists in both admin and user flows, diff them: the admin flow's pending-user session flag reveals the missing server-side binding in the public flow. Also check whether the 2FA endpoint has its own lockout independent of the login throttle.

