GHSA-98gw-w575-h2ph — thorsten/phpmyfaq
GHSA-98gw-w575-h2ph is a Improper Input Validation vulnerability in thorsten/phpmyfaq. A fix is available for thorsten/phpmyfaq — see the affected versions and patch details below.
phpMyFAQ is Vulnerable to Stored XSS via Unsanitized Email Field in Admin FAQ Editor
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
Real-World Exposure
thorsten/phpmyfaq🐘phpmyfaq/phpmyfaqReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
An unauthenticated attacker can submit a guest FAQ with an email address that is syntactically valid per RFC 5321 (quoted local part) yet contains raw HTML — for example "<script>alert(1)</script>"@evil.com. PHP's FILTER_VALIDATE_EMAIL accepts this email as valid. The email is stored in the database without HTML sanitization and later rendered in the admin FAQ editor template using Twig's |raw filter, which bypasses auto-escaping entirely.
Details
- PHP FILTER_VALIDATE_EMAIL accepts RFC-valid quoted local parts with dangerous characters
phpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/FaqController.php:99 $email = trim((string) Filter::filterVar($data->email, FILTER_VALIDATE_EMAIL)); PHP accepts "<script>alert(1)</script>"@evil.com as a valid email (RFC 5321 allows <, > inside quoted local parts). Confirmed: "<script>alert(1)</script>"@evil.com => string (valid, not false)
- Email stored raw without HTML sanitization
phpmyfaq/src/phpMyFAQ/Faq.php — email retrieved directly as $row->email from the database.
- Admin Twig template renders email with |raw
phpmyfaq/assets/templates/admin/content/faq.editor.twig:296 <input type="email" name="email" id="email" value="{{ faqData['email'] | raw }}" class="form-control">
Affected version: 4.2.0-alpha, commit f0dc86c8f
PoC
The reproduction of the vulnerability was implemented with the help of AI while reviewing the source code to generate the proof-of-concept. Please kindly note this for reference. Since the vulnerability has already been confirmed directly in the source code, the proof-of-concept code may be considered as a reference only.
Please extract the attached compressed file and proceed. poc.zip
- (docker compose -f docker-compose.yml down -v)
- docker compose -f docker-compose.yml up -d mariadb php-fpm nginx
- bash exploit.sh
- Access http://localhost:8888/admin/
- Log in with admin / Admin1234!
- After logging in, check whether the URL remains http://localhost:8888/admin/
- Go to Content → FAQ Administration → edit "poc" → alert popup should appear If it does not appear, you can also access it directly via: http://localhost:8888/admin/faq/edit/1/en
Impact
When an administrator opens /admin/faq/edit/{id}/{lang} to review the pending FAQ, the injected script executes in the admin's browser context. This allows an attacker to:
- Steal the administrator's session cookie → full admin account takeover
- Perform arbitrary admin actions (create users, modify content, change configuration)
- Pivot to further attacks on the server
The attack chain requires no authentication. By default, records.allowNewFaqsForGuests=true allows unauthenticated FAQ submission, and records.defaultActivation=false guarantees the administrator must visit the edit page to review it.
Note on captcha: The built-in captcha is enabled by default when the PHP gd extension is present (spam.enableCaptchaCode=true). This prevents fully automated exploitation but does not prevent a targeted manual attack — an attacker can solve the captcha once and submit the payload.
Credits
wooseokdotkim
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | thorsten/phpmyfaq | all versions | 4.1.1composer require thorsten/phpmyfaq:^4.1.1 |
| 🐘Packagist | phpmyfaq/phpmyfaq | all versions | 4.1.1composer require phpmyfaq/phpmyfaq:^4.1.1 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for thorsten/phpmyfaq, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update thorsten/phpmyfaq to 4.1.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-98gw-w575-h2ph is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-98gw-w575-h2ph can be triaged on real exposure rather than presence alone.
Tailored to GHSA-98gw-w575-h2ph. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-98gw-w575-h2ph in your dependencies?
O3 Security finds GHSA-98gw-w575-h2ph across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.