Description
The Favorites plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's 'user_favorites' shortcode in all versions up to, and including, 2.3.3 due to insufficient input sanitization and output escaping on user supplied attributes such as 'no_favorites'. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
Code
Fixed code: https://plugins.trac.wordpress.org/browser/favorites/trunk/app/API/Shortcodes/UserFavoritesShortcode.php
Vulnerable code: https://plugins.trac.wordpress.org/browser/favorites/trunk/app/API/Shortcodes/UserFavoritesShortcode.php?rev=2919192
Analysis
If we look at how this was discovered, it was proper through searching for unsafe concatenation of user strings in the file
functions.phpWe see that it does not use any escaping functions like
htmlentities, or WordPress specific functions like esc_html.if ( $html ) $out .= '<span class="simplefavorites-user-count" data-posttypes="' . $posttypes . '" data-siteid="' . $site_id . '">';
This can then be traced to follow the initialization of
$posttypes which is initialized from this line$posttypes = ( isset($filters['post_type']) ) ? implode(',', $filters['post_type']) : 'all';
Where
$filters['post_type'] was unsafely initialized without any escaping in UserFavoritesShortcode.phpprivate function setOptions($options) { $this->options = shortcode_atts([ 'user_id' => '', 'site_id' => '', 'include_links' => 'true', 'post_types' => '', 'include_buttons' => 'false', 'include_thumbnails' => 'false', 'thumbnail_size' => 'thumbnail', 'include_excerpts' => 'false', 'no_favorites' => '' ], $options); }
private function parsePostTypes() { if ( $this->options['post_types'] == "" ) return; $post_types = explode(',', $this->options['post_types']); $this->filters = ['post_type' => $post_types]; }
We see that
thumbnail_size is also not sanitized and accepts a string valuereturn $favorites->getFavoritesList( $this->options['include_buttons'], $this->options['include_thumbnails'], esc_attr($this->options['thumbnail_size']), $this->options['include_excerpts'], esc_attr($this->options['no_favorites']) );
This means that when a user enters the shortcode below, it will escape the HTML and can result in XSS
[user_favorites thumbnail_size="<script>alert(1)</script>]
The Fix
esc_attr was used in instances where HTML could be reflected to the front endreturn $favorites->getFavoritesList( $this->options['include_buttons'], $this->options['include_thumbnails'], esc_attr($this->options['thumbnail_size']), $this->options['include_excerpts'], esc_attr($this->options['no_favorites']) );
So now the shortcode string options like
thumbnail_size and no_favorites will be escapedRecreation
- Install WordPress locally
- Download the plugin and install it
Create a new post and add a shortcode in it with attributes
thumbnail_size and no_favoritesIf we look at the HTML that's being rendered, it properly escapes the special characters, so yea, seems like a valid fix :)