Introduction
An unauthenticated SQL injection in phpMyFAQ's public captcha API endpoint lets remote attackers extract password hashes, admin tokens, and SMTP credentials from the underlying database with nothing more than a crafted User-Agent header. The vulnerability has a CVSS score of 9.8 and a publicly available proof of concept, which makes it a serious risk for any organization running phpMyFAQ versions prior to 4.1.2.
phpMyFAQ is an open source FAQ web application built for PHP 8.3+ with support for MySQL, PostgreSQL, and other database backends. Thorsten Rinne maintains it under the MPL-2.0 license, and the project has 616 stars and 263 forks on GitHub. Organizations use it when they need a self-hosted knowledge base. It is less widely deployed than commercial alternatives, but it fills a niche for teams that want full control over their FAQ infrastructure.
Technical Information
The vulnerability is in the BuiltinCaptcha class, in two methods: garbageCollector() and saveCaptcha(). Both methods build SQL queries with sprintf() and interpolate the userAgent and ip fields directly from the incoming HTTP request. Neither field passes through Database::escape() or a prepared statement before it reaches the query.
On every request to the public GET /api/captcha endpoint, the getCaptchaImage() function unconditionally calls both vulnerable methods. That gives each request two separate SQL injection sinks:
- A
DELETEquery ingarbageCollector() - An
INSERTquery insaveCaptcha()
The endpoint requires no authentication, no CSRF token, and no session, and it enforces no rate limiting. Any remote attacker can reach both sinks with a GET request that carries a malicious User-Agent header.
Exploitation Flow
MySQL's query() method executes only one statement per call, which prevents query stacking. Time-based blind SQL injection still works fully. An attacker injects SQL functions such as SLEEP() or BENCHMARK() into the User-Agent header value.
Because there are two sinks, the injection is easy to confirm. A baseline request to the captcha endpoint completes in approximately 0.147 seconds. With a User-Agent header containing a payload like x' OR SLEEP(2) OR 'x, the response takes approximately 4.093 seconds. A delay of four seconds rather than two confirms that SLEEP(2) runs twice: once in the DELETE statement and once in the INSERT statement.
From there, attackers use single-bit boolean extraction to iterate through database rows and exfiltrate sensitive data. The primary targets are:
- The
faqusertable, which contains bcrypt password hashes for all registered users - The
faqconfigtable, which stores themain.phpMyFAQTokenadmin token and SMTP credentials
Payloads aimed at the DELETE path can also wipe arbitrary rows within the database connection's scope. That could destroy the entire faqcaptcha table and lock legitimate users out of captcha-protected functionality.
Recommended Code Fix
For organizations that maintain custom forks or cannot upgrade immediately, the fix is to route every user-controlled field through Database::escape() before interpolation. The patched garbageCollector() implementation should follow this pattern:
$db = $this->configuration->getDb(); $userAgent = $db->escape($this->userAgent); $language = $db->escape($this->configuration->getLanguage()->getLanguage()); $ip = $db->escape($this->ip); $delete = sprintf( "DELETE FROM %sfaqcaptcha WHERE useragent = '%s' AND language = '%s' AND ip = '%s'", Database::getTablePrefix(), $userAgent, $language, $ip ); $db->query($delete);
Apply the same escaping to saveCaptcha() and to any other code path that uses sprintf to build SQL statements from user-controlled input.
Affected Systems and Versions
All versions of phpMyFAQ prior to 4.1.2 are affected. Version 4.1.2, released on April 28, 2026, fixes the vulnerability. The flaw is present in any deployment where the GET /api/captcha endpoint is publicly reachable, which is the default configuration.
Vendor Security History
phpMyFAQ had a cluster of security disclosures in early 2026, which suggests the codebase is under active security review. The table below lists recent CVEs:
| CVE Identifier | Affected Versions | Description | Severity |
|---|---|---|---|
| CVE-2026-32629 | Prior to 4.1.1 | Unauthenticated attacker can submit a guest FAQ with an email | Unknown |
| CVE-2026-27836 | Prior to 4.0.18 | WebAuthn prepare endpoint creates new active user | Unknown |
| CVE-2026-24422 | 4.0.16 and below | Public API endpoints improperly expose sensitive user information | 7.5 HIGH |
| CVE-2026-24420 | 4.0.16 and below | Authenticated user without dlattachment permission access | Unknown |
The vendor has responded to each of these disclosures with a patch, a good sign for organizations assessing the project's security posture.



