Last May, I discovered a stored XSS vulnerability in "wpForo Forum", a WordPress plugin with over 20,000 active installations. Similar to other forum software like XenForo and phpBB, wpForo Forum allows users to create boards and topics for discussions on various subjects.

The vulnerability I found was in the avatar upload functionality. A user can upload an SVG file containing malicious Javascript because the sanitization was improperly implemented.

"wpForo Forum" uses WordPress' built-in HTML sanitizer wp_kses() to perform sanitization. The problem is that wp_kses() only strips the javascript: protocol from thehref attribute, but not from xlink:href. I discovered this by accident while experimenting around with the wp_kses() function.

I reported this issue with wp_kses() to WordPress' bug bounty program, but the WordPress team stated that this was not an issue since wp_kses() is not intended for XML sanitization.

If you're interested in doing security research on WordPress plugins like me, here are some really good resources to get started:

  • WordPress Security Research: A Beginner's Series: This series of articles discusses the basics of how WordPress works, how to setup a testing environment, and techniques for identifying vulnerabilities in WordPress plugins. These articles are written by the team at WordFence, who run the most popular WordPress firewall plugin as well as a bug bounty program for WordPress plugins.
  • Patchstack Academy: This website teaches the basics of WordPress security and how to find common vulnerabilities in WordPress plugins. Patchstack also runs a popular firewall plugin and a popular bug bounty program for WordPress plugins.

Proof of Concept

None
  1. Register as a forum user on a website with a vulnerable version of "wpForo Forum".
  2. Submit 3 posts and have them all approved by a moderator. You will need to do this to be able to edit your profile.
  3. Once you gain the permission to edit your profile, upload the SVG below as an avatar. This SVG is just a red circle which triggers javascript:alert('XSS') when clicked. Note that this value is emplaced in the xlink:href attribute.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <a xlink:href="javascript:alert('XSS')">
 <circle r="45" cx="100" cy="100" fill="red" />
 </a>
</svg>

4. When the SVG has been uploaded successfully, right-click on the profile picture and click "Open image in new tab".

5. Click on the red circle. An "XSS" popup should show up.

None

Root Cause Analysis

The upload_avatar() function calls a function wpforo_check_for_svg() to validate SVG files.

None
upload_avatar()

wpforo_check_for_svg() verifies that the uploaded file's extension belongs to an image and that the MIME type is either image/svg+xml or image/svg. If these checks are passed, wpforo_sanitize_svg() is called.

None
wpforo_check_for_svg()

wpforo_sanitize_svg() gzip decodes the SVG if it's gzipped, then passes the SVG to wp_foro_kses() for sanitization.

None
wpforo_sanitize_svg()

wpforo_kses() sets the allowed HTML tags and attributes depending on what is passed in the $keyargument (in this case 'svg'). Finally wp_kses() (Note: not the same function as wpforo_kses()) is called, which sanitizes the data.

None

In the case of SVGs, the list of allowed attributes includes xlink:href. But as we noted earlier, this is bad because wp_kses() does not strip the javascript: protocol from the xlink:href attribute! This is the root cause of the XSS vulnerability.

None
$allowed_attrs for SVGs

If you enjoyed this article, follow me on LinkedIn where I frequently post tips and tricks which have helped me be a better security engineer: https://www.linkedin.com/in/muhan-luo-620259171/

I also am currently working on projects on GitHub, some related to WordPress security, which you can find here: https://github.com/muhanLuo