GHSA-98GW-W575-H2PH
Vulnerability from github – Published: 2026-03-31 22:48 – Updated: 2026-04-06 17:18Summary
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 "alert(1)"@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 "alert(1)"@evil.com as a valid email (RFC 5321 allows <, > inside quoted local parts). Confirmed: "alert(1)"@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
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": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.0"
},
"package": {
"ecosystem": "Packagist",
"name": "thorsten/phpmyfaq"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.1.0"
},
"package": {
"ecosystem": "Packagist",
"name": "phpmyfaq/phpmyfaq"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-32629"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-31T22:48:45Z",
"nvd_published_at": "2026-04-02T15:16:38Z",
"severity": "MODERATE"
},
"details": "### Summary\nAn 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 \u2014 for example \"\u003cscript\u003ealert(1)\u003c/script\u003e\"@evil.com. PHP\u0027s 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\u0027s |raw filter, which bypasses auto-escaping entirely.\n\n### Details\n1. PHP FILTER_VALIDATE_EMAIL accepts RFC-valid quoted local parts with dangerous characters\n\nphpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/FaqController.php:99\n$email = trim((string) Filter::filterVar($data-\u003eemail, FILTER_VALIDATE_EMAIL));\nPHP accepts \"\u003cscript\u003ealert(1)\u003c/script\u003e\"@evil.com as a valid email (RFC 5321 allows \u003c, \u003e inside quoted local parts). Confirmed:\n\"\u003cscript\u003ealert(1)\u003c/script\u003e\"@evil.com =\u003e string (valid, not false)\n\n2. Email stored raw without HTML sanitization\n\nphpmyfaq/src/phpMyFAQ/Faq.php \u2014 email retrieved directly as $row-\u003eemail from the database.\n\n3. Admin Twig template renders email with |raw\n\nphpmyfaq/assets/templates/admin/content/faq.editor.twig:296\n\u003cinput type=\"email\" name=\"email\" id=\"email\" value=\"{{ faqData[\u0027email\u0027] | raw }}\" class=\"form-control\"\u003e\n\nAffected version: 4.2.0-alpha, commit f0dc86c8f\n\n\n### PoC\n**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.**\n\nPlease extract the attached compressed file and proceed.\n[poc.zip](https://github.com/user-attachments/files/25938058/poc.zip)\n\n\n0. (docker compose -f docker-compose.yml down -v)\n1. docker compose -f docker-compose.yml up -d mariadb php-fpm nginx\n2. bash exploit.sh\n-----\n1. Access http://localhost:8888/admin/\n2. Log in with admin / Admin1234!\n3. After logging in, check whether the URL remains http://localhost:8888/admin/\n4. Go to Content \u2192 FAQ Administration \u2192 edit \"poc\" \u2192 alert popup should appear\nIf it does not appear, you can also access it directly via:\nhttp://localhost:8888/admin/faq/edit/1/en\n\n\n\u003cimg width=\"1388\" height=\"239\" alt=\"\u1109\u1173\u110f\u1173\u1105\u1175\u11ab\u1109\u1163\u11ba 2026-03-12 \u110b\u1169\u1112\u116e 11 42 52\" src=\"https://github.com/user-attachments/assets/b6d5446f-4eba-4cb2-9284-1bca4855142e\" /\u003e\n\u003cimg width=\"1171\" height=\"92\" alt=\"\u1109\u1173\u110f\u1173\u1105\u1175\u11ab\u1109\u1163\u11ba 2026-03-12 \u110b\u1169\u1112\u116e 11 16 17\" src=\"https://github.com/user-attachments/assets/3578e429-7106-4616-92ed-4167816d40f0\" /\u003e\n\n\n### Impact\nWhen an administrator opens /admin/faq/edit/{id}/{lang} to review the pending FAQ, the injected script executes in the admin\u0027s browser context. This allows an attacker to:\n\n- Steal the administrator\u0027s session cookie \u2192 full admin account takeover\n- Perform arbitrary admin actions (create users, modify content, change configuration)\n- Pivot to further attacks on the server\n\nThe 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.\n\nNote 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 \u2014 an attacker can solve the captcha once and submit the payload. \n\n### Credits\nwooseokdotkim",
"id": "GHSA-98gw-w575-h2ph",
"modified": "2026-04-06T17:18:07Z",
"published": "2026-03-31T22:48:45Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-98gw-w575-h2ph"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32629"
},
{
"type": "PACKAGE",
"url": "https://github.com/thorsten/phpMyFAQ"
},
{
"type": "WEB",
"url": "https://github.com/thorsten/phpMyFAQ/releases/tag/4.1.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N/E:P",
"type": "CVSS_V4"
}
],
"summary": "phpMyFAQ is Vulnerable to Stored XSS via Unsanitized Email Field in Admin FAQ Editor"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.