14 Feb 2025

Hacker Probing For WordPress Plugin With Many Vulnerabilities That Wordfence and Other Providers Incorrectly Claimed Were Fixed Last Year

Today we saw what appeared to be a hacker probing for usage of the WordPress plugin WP Compress on our websites. The probing was done by requesting a file from the plugin if the plugin had existed on our website, /wp-content/plugins/wp-compress-image-optimizer/readme.txt. We don’t use that plugin on that website or any of them. So what might explain a hacker’s interest in the plugin? Last year the WordPress security provider Wordfence claimed that a vulnerability had been fixed in the plugin, of a type that sounds like it could explain a hacker’s interest. Here is part of their description:

This makes it possible for authenticated attackers, with subscriber-level permissions and above, to edit plugin settings, including storing cross-site scripting, in multisite environments.

The last part of that is odd. Why would that only be possible in “multisite environments?” They didn’t provide any further details to explain. They provided two links without any context as to what they were supposed to mean. The links showed a couple of things. First Wordfence had failed to vet the fix, as even the code being changed was still vulnerable. One of the links shows code being changed to add a capability check, notably missing is the addition of a nonce check or a pre-existing nonce checks. So there was still at least cross-site request forgery (CSRF) vulnerability. A little more checking showed that the capability check was incompletely applied to file being changed.

In the file /classes/mu.class.php, the following function had a capability check added (the current_user_can() part):

553
554
555
556
557
558
559
public function mu_save_site_settings()
{
	if (!current_user_can('manage_options')) {
		wp_send_json_error('Forbidden.');
	}
 
	global $wpc_siteID;

The next function should have also had that added, but it wasn’t:

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
public function mu_get_site_settings()
{
	global $wpc_siteID;
	$output = '';
 
	$wpc_siteID = $siteID = sanitize_title($_POST['siteID']);
	ob_start(); // begin collecting output
 
	if ($this->mu_is_connected($siteID)) {
		include WPS_IC_DIR . 'templates/mu/connected.php';
		#include WPS_IC_DIR . 'templates/mu/site-settings.php';
	} else {
		include WPS_IC_DIR . 'templates/mu/not-connected.php';
	}
 
	$output .= ob_get_clean(); // retrieve output from myfile.php, stop buffering
 
	wp_send_json_success($output);
}

So even as described by Wordfence, the issue had only been partially fixed. It turns out the issue is still exploitable even when not using WordPress Multisite.

Plugin Vulnerability Data Providers Don’t Vet Their Data

That Wordfence got things so wrong runs counter to their claim to have a team of experts and their CEO Mark Maunder’s claim that their vulnerability “data is impeccable.”

They were not alone in that. WPScan, which also claims to have a team of experts, claimed the issue was fixed:

While also somehow claiming that they hadn’t verified it (somehow that is a miscellaneous detail):

Patchstack, also claims to have a team of experts, yet they also claimed it was fixed:

They are claiming to have provided a “virtual patch”, so either they knew the fix was incomplete and claimed otherwise, or their virtual patch is incomplete as well.

The reality is that WPScan and Patchstack simply copy information from Wordfence without vetting, despite Wordfence’s data being known to be unreliable going back to before it was public. (Wordfence also copies data from the other providers without vetting it.)

So Much is Vulnerable

When trying to determine what a hacker might be interested in exploiting related to this, you run into the problem that is so much code that is accessible it is hard to guess what a hacker might be interested. But to provide an example of what Wordfence and the other security providers somehow missed, let’s take a look at a function to change the plugin’s settings. In the file /classes/ajax.class.php, the function wps_ic_settings_change() is registered to be accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:

126
$this->add_ajax('wps_ic_settings_change');

That function doesn’t include a capability check or a nonce check to limit access to changing the settings:

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/**
 * Change Settings Value
 */
public function wps_ic_settings_change()
{
	global $wps_ic;
 
	$what = sanitize_text_field($_POST['what']);
	$value = sanitize_text_field($_POST['value']);
	$checked = sanitize_text_field($_POST['checked']);
	$checkbox = sanitize_text_field($_POST['checkbox']);
 
 
	$options = new wps_ic_options();
	$settings = $options->get_settings();
 
	if ($what == 'thumbnails') {
		if (!isset($value) || empty($value)) {
			$settings['thumbnails'] = [];
		} else {
			$settings['thumbnails'] = [];
			$value = rtrim($value, ',');
			$value = explode(',', $value);
			foreach ($value as $i => $thumb_size) {
				$settings['thumbnails'][$thumb_size] = 1;
			}
		}
	} else {
		if ($what == 'autopilot') {
			if ($checked == 'checked') {
			} else {
				$settings['otto'] = 'automated';
			}
		}
 
		if ($checkbox == 'true') {
			if ($checked === 'false') {
				$settings[$what] = 0;
			} else {
				$settings[$what] = 1;
			}
		} else {
			$settings[$what] = $value;
		}
	}
 
	if ($what == 'live_autopilot') {
		if ($value == '1') {
			// Enabline Live, clear local queue
			delete_option('wps_ic_bg_stop');
			delete_option('wps_ic_bg_process_stop');
			delete_option('wps_ic_bg_stopping');
			delete_option('wps_ic_bg_process');
			delete_option('wps_ic_bg_process_done');
			delete_option('wps_ic_bg_process_running');
			delete_option('wps_ic_bg_process_stats');
			delete_option('wps_ic_bg_last_run_compress');
			delete_option('wps_ic_bg_last_run_restore');
		}
	} elseif ($what == 'css' || $what == 'js') {
		// Purge CSS/JS Cache
		$this->purge_cdn_assets();
	}
 
	self::$cacheIntegrations->purgeAll();
 
	update_option(WPS_IC_SETTINGS, $settings);
 
	wp_send_json_success();
}

So anyone logged in to WordPress can change the plugin’s settings. An attacker could cause anyone logged in to change settings through CSRF as well.

More Issues Probably Exists

Our Plugin Security Scorecard and Plugin Security Checker tools flag the plugin as possibly having yet more security issues.

Developer Notified

We reached out to the developer earlier today to let them known about the incomplete fix and offered to help them address it.

Free Warning

As some part of the vulnerability might be targeted by hackers, we are adding accurate data on it to the free data that comes with our Plugin Vulnerabilities plugin.


Plugin Security Scorecard Grade for Patchstack

Checked on March 5, 2025
D

See issues causing the plugin to get less than A+ grade


Plugin Security Scorecard Grade for WPScan

Checked on April 12, 2025
F

See issues causing the plugin to get less than A+ grade

Leave a Reply

Your email address will not be published. Required fields are marked *