Vulnerability GHSA-rw77-vq4g-x3hp
Summary
phpMyFAQ has SQL Injection in `StopWords::add()` — Unescaped Stop Word Insertion
Details
Summary
The StopWords::add() method in phpMyFAQ builds a SQL INSERT statement using sprintf() and inserts the user-supplied stop word value directly into the query string without calling the application's database escaping function on it. A sibling method, StopWords::update(), which modifies an existing stop word, correctly escapes the same kind of input. The omission is isolated to the add() (insert) code path.
An authenticated administrator who can reach the stop-word management feature can submit a crafted value as the "word" parameter that breaks out of the SQL string literal and injects arbitrary SQL, including statements to drop tables, exfiltrate data, or modify other rows in the database.
Proof of Concept
Precondition: Attacker has valid administrator credentials (or has otherwise obtained an authenticated administrator session, e.g. via a separate session-hijacking or CSRF vector).
Attack steps:
-
Authenticate to the phpMyFAQ administration panel.
-
Navigate to the Stop Words management feature.
-
Submit a new stop word with the following value instead of a normal word:
test', 'en'); DROP TABLE faqstopwords; -- -
The resulting SQL statement sent to the database becomes (table/column names approximate, based on the traced
sprintftemplate):INSERT INTO faqstopwords VALUES(1, 'en', 'test', 'en'); DROP TABLE faqstopwords; --') -
The injected
DROP TABLE faqstopwords;statement executes as a second SQL statement (subject to the database driver/PDO configuration permitting multi-statement execution; even where multi-statement execution is disabled, the same injection point allows classic single-statement SQLi techniques such asUNION-based data extraction or boolean/time-based blind injection against other tables the database user can access).
Root Cause
The codebase's established pattern for this class (StopWords.php) is to escape all string values via $this->configuration->getDb()->escape($value) before placing them into a sprintf()-built SQL string. This pattern is correctly applied to:
$this->languageinadd()$wordinupdate()
It is not applied to $word in add(). This is a single-line omission, not a structural design flaw — the safe pattern already exists in the same file and the same class, just inconsistently applied across the two methods that handle the same input type.
Related Vulnerabilities
Other vulnerabilities affecting the same packages