phpMyFAQ, an open-source FAQ system used by thousands of sites, ships with a flaw that lets unauthenticated attackers dump every custom page in its database. Attackers craft search queries with SQL LIKE wildcards—percent (%) and underscore (_)—that bypass weak sanitization. A simple three-character search like _%_ matches virtually all records, exposing titles and full content meant for targeted searches only.
This hits searchCustomPages() in phpMyFAQ/src/phpMyFAQ/Search.php, lines 226-240. The code escapes the search term with MySQLi’s real_escape_string(), which handles quotes and backslashes fine for general SQL. But it ignores LIKE-specific metacharacters. So % still matches any string sequence, and _ any single character.
Developers split the escaped term into words, skip those under three characters, then build LIKE clauses: page_title LIKE '%word%' OR content LIKE '%word%'. An attacker slips in wildcards within a longer “word.” Search a_b and it becomes '%a_b%', matching aXb, a1b, anything with one char between a and b. Worse, _%_ turns into '%_%_%', grabbing every custom page with at least one character—basically everything.
$escapedSearchTerm = $this->configuration->getDb()->escape($searchTerm);
$searchWords = explode(' ', $escapedSearchTerm);
$searchConditions = [];
foreach ($searchWords as $word) {
if (strlen($word) <= 2) {
continue;
}
$searchConditions[] = sprintf(
"(page_title LIKE '%%%s%%' OR content LIKE '%%%s%%')",
$word, $word
);
}
Proof-of-concept is dead simple. Hit the public search endpoint—enabled by default—and submit _%_. No login needed. The query scans the faqcustompages table, spilling all entries. Sites using custom pages for internal docs, pricing, or sensitive FAQs just handed attackers a full export.
Why This Matters
phpMyFAQ powers knowledge bases for businesses, non-profits, even some government sites—over 100,000 installs per estimates from usage trackers like Wappalyzer. Custom pages extend it beyond standard FAQs, often holding proprietary info. Public search seems harmless, but this turns it into a data leak vector.
Attackers automate this: loop wildcards, scrape results, rebuild the database offline. No exploits beyond basic HTTP; works on any vulnerable install. CVSS? Likely 6.5+ for unauth info disclosure. It’s a classic mistake—treating general SQL escaping as enough for pattern matching. Happens because devs copy-paste search logic without LIKE specifics.
Skeptical take: If your custom pages are truly public-facing, damage is low. But many admins assume search filters hide bulk content. Reality check—crawlers already index public pages, but this bypasses any frontend limits, dumping raw DB content faster.
Fix It Now
Patch escapes LIKE wildcards before interpolating. Double backslashes first, then replace:
$word = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $word);
Better: Ditch string building. Use prepared statements with bound parameters. MySQLi supports it; bind the escaped word directly into LIKE placeholders. Update to latest phpMyFAQ if available—check GitHub repo thorsten/phpmyfaq for commits post-advisory.
Broader lesson: Audit all LIKE/USING clauses. Tools like SQLMap flag injections, but manual review catches pattern flaws. Run grep -r "LIKE.*%" on your codebase. For phpMyFAQ users, disable public search or custom pages if unused. Test your instance: search _%_ today.
This isn’t zero-day hype—it’s a 2023-era slip-up in maintained OSS. Fix fast; exposure scales with your data volume.