17 Aug 2023

Update to WordPress Plugin Allows Logged-In Users to Install Malicious Plugins

One way we help to improve the security of WordPress plugins, not just for customers of our service, but for everyone using them, is our proactive monitoring of changes made to plugins in the Plugin Directory to try to catch serious vulnerabilities. Through that, we caught one of those vulnerabilities, a vulnerability in the plugin Disable Fullscreen Mode that allows those logged in to WordPress to install arbitrary plugins. The plugin doesn’t have to come from the WordPress plugin directory, so an attacker can install an entirely malicious plugin.

We now are also running all the code in the plugins used by our customers through that monitoring system on a weekly basis to provide additional protection for them.

We tested and confirmed that our firewall plugin for WordPress protected against exploitation of this vulnerability, even before we discovered the vulnerability, as part of its protection against zero-day vulnerabilities.

Authenticated Plugin Installation

After not receiving any updates since 2020, this week the plugin was updated. One change was to introduce a new admin page that recommends installing a couple of other plugins by the developer. The underlying code, though, doesn’t restrict what plugins can be installed or properly restrict who has access to the functionality. That isn’t what you hope to see from a plugin developer who also has a plugin with 40,000+ installs.

In the new file /Libs/Recommended.php, the function jltdfsm_recommended_upgrade_plugin() is registered to be accessible to those logged in to WordPress:

43
add_action( 'wp_ajax_jltdfsm_recommended_upgrade_plugin', array( $this, 'jltdfsm_recommended_upgrade_plugin' ) );

That function allows installing a plugin from a URL specified by the POST input “plugin”:

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
public function jltdfsm_recommended_upgrade_plugin() {
	try {
		require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
		require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
 
		if ( isset( $_POST['plugin'] ) ) {
			$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
 
			if ( ! wp_verify_nonce( $nonce, 'jltdfsm_recommended_nonce' ) ) {
				wp_send_json_error( array( 'mess' => __( 'Nonce is invalid', 'disable-fullscreen-mode' ) ) );
			}
			$plugin   = sanitize_text_field( wp_unslash( $_POST['plugin'] ) );
			$type     = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : 'install';
			$skin     = new \WP_Ajax_Upgrader_Skin();
			$upgrader = new \Plugin_Upgrader( $skin );
 
			if ( 'install' === $type ) {
				$result = $upgrader->install( $plugin );

Later in the function’s code, the plugin will be activated as well. Another similarly accessible function in the file, jltdfsm_recommended_activate_plugin(), also allows activating arbitrary plugins.

The one restriction on this is that a valid nonce, which prevents cross-site request forgery (CSRF), needs to be provided. Unfortunately, instead of only being accessible on the admin page where the plugins are intended to be installed, that is made accessible on admin pages normally accessible to all WordPress users.

WordPress Causes Full Disclosure

As a protest of the moderators of the WordPress Support Forum’s continued inappropriate behavior we changed from reasonably disclosing to full disclosing vulnerabilities for plugins in the WordPress Plugin Directory in protest, until WordPress gets that situation cleaned up, so we are releasing this post and then leaving a message about that for the developer through the WordPress Support Forum. (For plugins that are also in the ClassicPress Plugin Directory, we will follow our reasonable disclosure policy.)

You can notify the developer of this issue on the forum as well.

After four years, the moderators have finally tacitly admitted they were behaving inappropriately and have made moves to fix the problems (though incompletely), so these full disclosures can be ended if they simply restore access to our accounts and plugins in the Plugin Directory. Hopefully that takes less than four years.

Update: To clear up the confusion where developers claim we hadn’t tried to notify them through the Support Forum (while at the same time moderators are complaining about us doing just that), here is the message we left for this vulnerability:

Proof of Concept

The following proof of concept will install the plugin Akismet and activate it, when logged in to WordPress.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[nonce]” with the nonce value from “recommended_nonce” in the source code of admin pages.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=jltdfsm_recommended_upgrade_plugin" method="POST">
<input type="hidden" name="nonce" value="[nonce]" />
<input type="hidden" name="plugin" value="https://downloads.wordpress.org/plugin/akismet.5.2.zip" />
<input type="submit" value="Submit" />
</form>
</body>

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.

Leave a Reply

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