Automated Threat Modeling

Brief Summary: CVE-2026-46364 — Unauthenticated SQL Injection in phpMyFAQ's Captcha API via User-Agent Header

A short review of CVE-2026-46364, a critical (CVSS 9.8) unauthenticated SQL injection in phpMyFAQ's BuiltinCaptcha class. Attackers can extract credentials and tokens by sending crafted User-Agent headers to the public captcha API endpoint.

CVE Analysis

5 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-15

Brief Summary: CVE-2026-46364 — Unauthenticated SQL Injection in phpMyFAQ's Captcha API via User-Agent Header
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

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:

  1. A DELETE query in garbageCollector()
  2. An INSERT query in saveCaptcha()

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 faquser table, which contains bcrypt password hashes for all registered users
  • The faqconfig table, which stores the main.phpMyFAQToken admin 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.

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 IdentifierAffected VersionsDescriptionSeverity
CVE-2026-32629Prior to 4.1.1Unauthenticated attacker can submit a guest FAQ with an emailUnknown
CVE-2026-27836Prior to 4.0.18WebAuthn prepare endpoint creates new active userUnknown
CVE-2026-244224.0.16 and belowPublic API endpoints improperly expose sensitive user information7.5 HIGH
CVE-2026-244204.0.16 and belowAuthenticated user without dlattachment permission accessUnknown

The vendor has responded to each of these disclosures with a patch, a good sign for organizations assessing the project's security posture.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization