21 Apr 2022

Authenticated Post Deletion Vulnerability in Toolset Types WordPress Plugin

As part of our recent focus on providing better information to customers of our main service about the security of plugins they use, we extended monitoring we already did on the closure of the most popular WordPress plugins on WordPress’ plugin directory to those being used by our customers. We monitor those closures because they are often caused by security vulnerabilities, sometimes very serious vulnerabilities. That monitoring notified us yesterday that a customer used plugin Toolset Types has been closed. According to the message on the plugin’s page, it was closed in 2019, so this must be a new customer or a website newly using the plugin:

This plugin has been closed as of April 4, 2019 and is not available for download. This closure is permanent. Reason: Author Request.

The reason listed in that is rather vague, but we found the developer had removed it as they had moved away from a freemium model.

While the plugin wasn’t closed because of a security issue, we wanted to make sure there wasn’t a glaring security issue that we should warn our customers about if they were still relying on the last free version. What we found is that plugin, at least at that point, had a lot of problems with security. Our Plugin Security Checker flags a long list of potential issues with it:

In looking at one of the items flagged by that, we found a vulnerability and there likely are additional issues. Unless the security has been significantly improved since then, the chances of issues still existing would be high.

While the plugin is closed on the plugin directory, as we noted recently, they can still be downloaded, which is occurring with this plugin:

Authenticated Post Deletion

The plugin registers the function wpcf_ajax_embedded() to be accessible to anyone logged in to WordPress:

11
add_action( 'wp_ajax_wpcf_ajax', 'wpcf_ajax_embedded' );

What is flagged by our Plugin Security Checker in that function, which is located in the file /vendor/toolset/types/embedded/includes/ajax.php, is that it allows checking for a valid nonce with the nonce to be checked specified by the requestor:

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function wpcf_ajax_embedded() {
 
	// If this is true, we won't die() at the end of this method.
	$fallthrough = false;
 
	if ( isset( $_REQUEST['_typesnonce'] ) ) {
        if ( !wp_verify_nonce( $_REQUEST['_typesnonce'], '_typesnonce' ) ) {
            die( 'Verification failed (1)' );
        }
    } else {
 
        if (
            !isset( $_REQUEST['_wpnonce'] )
            || !wp_verify_nonce( $_REQUEST['_wpnonce'], $_REQUEST['wpcf_action'] ) 
        ) {
            die( 'Verification failed (2)' );
        }
    }

That isn’t a vulnerability in an instance where access is properly restricted with capabilities checks, since nonces are only intended to prevent cross-site request forgery (CSRF), not as an access control. But many plugins are lacking capabilities checks, so that type of issue is a big concern.

In this situation, we found that there were capabilities checks being done, but we found at least one didn’t provide the required restriction and the rest of the code didn’t restrict things either.

One of the actions accessible through that function is to delete a child post:

262
263
264
265
266
case 'pr_delete_child_post':
	require_once WPCF_EMBEDDED_ABSPATH . '/includes/post-relationship.php';
	$output = 'Passed wrong parameters';
	if ( current_user_can( 'edit_posts' ) && isset( $_GET['post_id'] ) ) {
		$output = wpcf_pr_admin_delete_child_item( intval( $_GET['post_id'] ) );

That checks if the user making the request has ability to edit_posts, which users down to the Contributor role have. The code should have instead checked if the user has the ability to edit the specific post being specified by the GET input “post_id”. The value of that input is passed to the function wpcf_pr_admin_delete_child_item(), which doesn’t have any restrictions on what posts can be deleted through it:

357
358
function wpcf_pr_admin_delete_child_item( $post_id ) {
    wp_delete_post( $post_id, true );

As post IDs are integers, an attacker with access to a low level WordPress account could iterate through all post IDs and delete everything stored that way (which isn’t just WordPress posts and pages). Because the second parameter passed to wp_delete_post() is “true”, the post will not be sent to trash, but instead be permanently deleted, making that insecurity worse.

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.

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, but considering that they believe that having plugins, which have millions installs, remain in the Plugin Directory despite them knowing they are vulnerable is “appropriate action”, something is very amiss with them (which is even more reason the moderation needs to be cleaned up).

If the moderation is cleaned up, it would also allow the possibility of being able to use the forum to start discussing fixing the problems caused by the very problematic handling of security by the team running the Plugin Directory, discussions which they have for years shut down through their control of the Support Forum.

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:

Is It Fixed?

If you are reading this post down the road the best way to find out if this vulnerability or other WordPress plugin vulnerabilities in plugins you use have been fixed is to sign up for our service, since what we uniquely do when it comes to that type of data is to test to see if vulnerabilities have really been fixed. Relying on the developer’s information can lead you astray, as we often find that they believe they have fixed vulnerabilities, but have failed to do that.

Proof of Concept

The following proof of concept will delete the specified post, when logged in to WordPress as a Contributor.

Make sure to replace “[path to WordPress]” with the location of WordPress, “[post ID]” with ID of the post to be deleted, and “[nonce]” with the nonce value listed in the line that begins “var types” in the source of WordPress’ admin dashboard.

http://[path to WordPress]/wp-admin/admin-ajax.php?action=wpcf_ajax&wpcf_action=pr_delete_child_post&post_id=[post ID]&_typesnonce=[nonce]

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.

Need Continued Support for a Closed Plugin?

Does your website depend on a WordPress plugin that is no longer being supported by the original developer? With our Abandoned WordPress Plugin Maintenance Service, we can maintain the plugin for you, so you can safely use the plugin going forward.

Leave a Reply

Your email address will not be published.