CVE-2026-46364: Verified Repro With Script Download
CVE-2026-46364: phpMyFAQ: unauthenticated SQL injection via User-Agent header in captcha API
CVE-2026-46364 is verified against thorsten/phpmyfaq · composer. Affected versions: < 4.1.2. Fixed in 4.1.2. Vulnerability class: SQLi. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00144.
What Is CVE-2026-46364?
CVE-2026-46364 is a critical unauthenticated SQL injection in phpMyFAQ via the User-Agent HTTP header in the captcha API. Pruva reproduced it (reproduction REPRO-2026-00144).
CVE-2026-46364 Severity & CVSS Score
CVE-2026-46364 is rated critical severity, with a CVSS base score of 9.8 out of 10.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected thorsten/phpmyfaq Versions
thorsten/phpmyfaq · composer versions < 4.1.2 are affected.
How to Reproduce CVE-2026-46364
pruva-verify REPRO-2026-00144 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00144/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-46364
Reproduced by Pruva's autonomous agents — 328 tool calls over 1h 2m. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-46364
Summary
phpMyFAQ versions prior to 4.1.2 contain an unauthenticated SQL injection vulnerability in the BuiltinCaptcha class. The User-Agent HTTP header is interpolated directly into SQL DELETE and INSERT queries in garbageCollector() and saveCaptcha() without any escaping or parameterization. An attacker can exploit this by sending a crafted User-Agent header to the /api/captcha endpoint, causing arbitrary SQL to execute in the context of the application's database connection.
Impact
- Package/component affected:
thorsten/phpmyfaq—phpMyFAQ\Captcha\BuiltinCaptcha - Affected versions:
< 4.1.2(vulnerable);4.1.2and later (fixed) - Risk level: Critical (CVSS 3.1 base 9.8)
- Consequences: Unauthenticated time-based blind SQL injection via the public
GET /api/captchaendpoint. An attacker can read, modify, or delete database contents, bypass authentication, or execute administrative actions depending on the database privileges of the application user.
Root Cause
In BuiltinCaptcha::garbageCollector() and BuiltinCaptcha::saveCaptcha() (phpMyFAQ 4.1.1), the $this->userAgent, $this->ip, $this->code, and language values are concatenated directly into SQL strings using sprintf():
$delete = sprintf(
"DELETE FROM %sfaqcaptcha WHERE useragent = '%s' AND language = '%s' AND ip = '%s'",
Database::getTablePrefix(),
$this->userAgent,
$this->configuration->getLanguage()->getLanguage(),
$this->ip,
);
Because the User-Agent value is controlled by the HTTP client and is not escaped before interpolation, an attacker can inject SQL syntax (e.g., ' OR '1'='1) that alters the query's semantics.
The fix commit (545bdffb11244a4741cd29ac909a849c9a6a2e53 in the 4.1.2 release timeline) introduces an escapeQueryValue() helper that calls $this->configuration->getDb()->escape() on each untrusted value before concatenation, effectively neutralizing the injection vector.
Reproduction Steps
- Run
repro/reproduction_steps.sh - The script clones the phpMyFAQ repository, checks out the vulnerable tag
4.1.1, installs dependencies via Composer, and starts a built-in PHP server (php -S) hosting a minimal captcha-generating endpoint. - The script sends an HTTP
GETrequest tohttp://localhost:8766/with a maliciousUser-Agent: test' OR '1'='1header. - The captcha endpoint instantiates
BuiltinCaptchafrom the actual phpMyFAQ source code and callsgetCaptchaImage(), which internally triggersgarbageCollector()→saveCaptcha(). - The script inspects the SQLite
faqcaptchatable to verify the payload was executed as SQL rather than stored as a literal string. - The script repeats steps 2–5 against the fixed tag
4.1.2to confirm the payload is now escaped and stored literally.
Expected Evidence
- Vulnerable (4.1.1): The
useragentcolumn in the SQLite database contains1(the boolean result of the injected SQL expression'1'='1'), proving the payload was evaluated as SQL. - Fixed (4.1.2): The
useragentcolumn contains the literal stringtest' OR '1'='1, proving the input was escaped before reaching the query.
Evidence
Log file
logs/repro.log
Key excerpts
=== Testing vulnerable version 4.1.1 ===
Database useragent value: 1
CONFIRMED: 4.1.1 is VULNERABLE (User-Agent payload executed as SQL, boolean result stored)
=== Testing fixed version 4.1.2 ===
Database useragent value: test' OR '1'='1
CONFIRMED: 4.1.2 is FIXED (User-Agent stored as literal string)
Runtime manifest
repro/runtime_manifest.jsoncaptures structured evidence:vulnerable_useragent_value:"1"fixed_useragent_value:"test' OR '1'='1"payload:"test' OR '1'='1"target_url:http://localhost:8766/http_status:200
Recommendations / Next Steps
- Upgrade immediately to phpMyFAQ 4.1.2 or later. The patch adds escaping via
$this->configuration->getDb()->escape()for all untrusted values injected into captcha SQL queries. - Review other query-building code in the codebase for similar unsanitized
sprintf()patterns, especially inFaq.php,Relation.php,Search.php,Tags.php, andSearchDatabase.php, which were also refactored in the 4.1.2 release. - Add parameterized query enforcement as a code-quality rule (e.g., via static analysis or custom linters) to prevent raw string concatenation with user input in SQL generation.
- Regression testing: The existing
BuiltinCaptchaTestin the project now includestestSaveCaptchaEscapesUserAgentAndIpValues()andtestGarbageCollectorEscapesUserAgentAndIpValues(), which should be run in CI for every release.
Additional Notes
- Idempotency:
repro/reproduction_steps.shwas executed twice consecutively on a clean environment; both runs produced identical results, confirming the script is idempotent. - Edge cases / limitations: The reproduction uses SQLite because it is lightweight and requires no external database server. The injection vector is database-agnostic; MySQL/MariaDB users would observe similar behavior (with
SLEEP()-based time delays as the primary signal). PostgreSQL and SQL Server backends are also affected because the root cause is unsanitized string interpolation, not a dialect-specific feature. - No authentication required: The reproduction hits an unauthenticated endpoint, matching the advisory's assessment that no login, token, or user interaction is needed to exploit this vulnerability.
CVE-2026-46364 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-46364
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-46364
Upgrade thorsten/phpmyfaq · composer to 4.1.2 or later.
FAQ: CVE-2026-46364
Is CVE-2026-46364 exploitable without authentication?
Which versions of phpMyFAQ are affected by CVE-2026-46364, and where is it fixed?
How can I reproduce CVE-2026-46364?
References for CVE-2026-46364
Authoritative sources for CVE-2026-46364 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.