50,000 WordPress Sites affected by Arbitrary File Upload Vulnerability in Ninja Forms – File Upload WordPress Plugin

On January 8th, 2026, we received a submission for an Arbitrary File Upload vulnerability in Ninja Forms – File Upload, a WordPress plugin with an estimated 50,000 active installations. This vulnerability makes it possible for an unauthenticated attacker to upload arbitrary files to a vulnerable site and achieve remote code execution.

Props to Sélim Lanouar (whattheslime) who discovered and responsibly reported this vulnerability through the Wordfence Bug Bounty Program. This researcher earned a bounty of $2,145.00 for this discovery. Our mission is to secure WordPress through defense in depth, which is why we are investing in quality vulnerability research and collaborating with researchers of this caliber through our Bug Bounty Program. We are committed to making the WordPress ecosystem more secure through the detection and prevention of vulnerabilities, which is a critical element to the multi-layered approach to security.

Wordfence Premium, Wordfence Care, and Wordfence Response users received a firewall rule to protect against any exploits targeting this vulnerability on January 8, 2026. Sites using the free version of Wordfence received the same protection 30 days later on February 7, 2026.

We provided full disclosure details to the Saturday Drive team instantly through our Wordfence Vulnerability Management Portal on January 8, 2026. The developer released the first patch on February 10, 2026, and the second patch on March 19, 2026.

We urge users to update their sites with the latest patched version of Ninja Forms – File Upload, version 3.3.27 at the time of this publication, as soon as possible.


🔥🔥🔥 Triple Threat Bug Bounty Challenge 🔥🔥🔥
Hunt High Threat vulnerabilities and earn triple the incentives!

Now through April 6, 2026, earn three stacked bonuses on all valid submissions from our ‘High Threat Vulnerabilities’ list:

  • 💰 2x all high threat vulnerability bounties (excluding 5,000,000+ installs)
  • 📈 +30% bonus for high threat vulnerabilities in software with 30,000+ active installs (excluding 5,000,000+ installs)
  • 🎯 $300 extra for every 3 High Threat vulnerabilities submitted (minimum of 1,000 installs)

Use the Bounty Estimator to see what rewards are possible through the promotion.

Submit through our Bug Bounty Program today to maximize your impact and your payout.


Vulnerability Summary from Wordfence Intelligence

CVSS Rating
9.8 (Critical)
Affected Version(s)
<= 3.3.26
Patched Version
3.3.27
Bounty
$2,145.00
Affected Software
Ninja Forms – File Uploads [ninja-forms-uploads]
The Ninja Forms – File Uploads plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in the ‘NF_FU_AJAX_Controllers_Uploads::handle_upload’ function in all versions up to, and including, 3.3.26. This makes it possible for unauthenticated attackers to upload arbitrary files on the affected site’s server which may make remote code execution possible. Note: The vulnerability was partially patched in version 3.3.25 and fully patched in version 3.3.27.

Technical Analysis

Ninja Forms – File Upload is an addon WordPress plugin, which provides a file upload feature for the Ninja Forms plugin.

Examining the code reveals that the plugin uses the handle_upload() function in the NF_FU_AJAX_Controllers_Uploads class to upload files to the WordPress website.

public function handle_upload() {
	$field_id = $this->get_field_id();

	if ( ! $field_id ) {
		$this->_errors[] = __( 'No field ID supplied', 'ninja-forms-uploads' );
		$this->_respond();
	}

	$result = check_ajax_referer( 'nf-file-upload-' . $field_id, 'nonce', false );
	if ( false === $result ) {
		$this->_errors[] = __( 'Nonce error', 'ninja-forms-uploads' );
		$this->_respond();
	}

	$form_id = $this->get_form_id();

	if ( ! $form_id ) {
		$this->_errors[] = __( 'No form ID supplied', 'ninja-forms-uploads' );
		$this->_respond();
	}

	$field_instance_id = filter_input( INPUT_POST, 'field_id' );

	$files_key = 'files-' . str_replace( '.', '_', $field_instance_id );
	if ( ! isset( $_FILES[ $files_key ] ) ) {
		$this->_errors[] = $this->code_to_message( '' );
		$this->_respond();
	}

	$this->_data['files'] = $this->_prepare( $_FILES[ $files_key ] );

	$this->_process();
	$this->_respond();
}

This function calls the _process() function in the NF_FU_AJAX_Controllers_Uploads class, which uses the move_uploaded_file() function to save the uploaded file to the uploads folder.

protected function _process() {
	foreach ( $this->_data['files'] as $key => $file ) {

		$file_name = $this->get_filename_from_chunk();
		if ( $file_name ) {
			$file['name'] = $file_name;
		}

		$file_size = $this->get_file_size_from_chunk();
		if ( $file_size ) {
			$file['size'] = $file_size;
		}

		if ( false === $this->_validate( $file ) ) {
			unset( $this->_data['files'][ $key ] );
			@unlink( $file['tmp_name'] );
			continue;
		}

		$file_key     = strtolower( str_replace( array( ' ', '.' ), '_', $file['name'] ) );
		$new_tmp_name = filter_input( INPUT_POST, $file_key );

		// Security fix: Sanitize user-provided filename to prevent path traversal
		if ( ! empty( $new_tmp_name ) ) {
			// Remove any path traversal attempts - use only the basename
			$new_tmp_name = basename( $new_tmp_name );
			// Apply WordPress filename sanitization
			$new_tmp_name = sanitize_file_name( $new_tmp_name );

			// Validate destination extension against blacklist
			$dest_extension = strtolower( pathinfo( $new_tmp_name, PATHINFO_EXTENSION ) );
			if ( self::blacklisted( self::get_extension_blacklist(), $dest_extension ) ) {
				$this->_errors[] = sprintf(
					__( 'File extension of %s not allowed', 'ninja-forms-uploads' ),
					$dest_extension
				);
				unset( $this->_data['files'][ $key ] );
				@unlink( $file['tmp_name'] );
				continue;
			}
		}

		if ( empty( $new_tmp_name ) ) {
			$new_tmp_name = $this->get_temp_filename( $file['name'] );
		}

		$new_tmp_file_path = NF_File_Uploads()->controllers->uploads->get_path( $new_tmp_name, true );

		$append_file = $this->get_content_range() && is_file( $new_tmp_file_path ) && $file['size'] > NF_FU_Helper::get_file_size( $new_tmp_file_path );

		if ( $append_file ) {
			$result = file_put_contents( $new_tmp_file_path, fopen( $file['tmp_name'], 'r' ), FILE_APPEND );
		} else {
			$result = move_uploaded_file( $file['tmp_name'], $new_tmp_file_path );
		}

		if ( false === $result ) {
			unset( $this->_data['files'][ $key ] );
			$this->_errors[] = __( 'Unable to move uploaded temp file', 'ninja-forms-uploads' );

			continue;
		}

		// Schedule a clean up of the file if the form doesn't get submitted
		wp_schedule_single_event( apply_filters( 'ninja_forms_uploads_temp_file_delete_time', time() + HOUR_IN_SECONDS ), 'nf_fu_delete_temporary_file', array( $new_tmp_file_path ) );

		$this->_data['files'][ $key ]['tmp_name'] = $new_tmp_name;
		$this->_data['files'][ $key ]['new_tmp_key'] = $file_key;
	}
}

Although the function includes a file type check in the source filename within the _validate() function in the NF_FU_AJAX_Controllers_Uploads class, it is not sufficient. Unfortunately, the function does not include any file type or extension checks on the destination filename before the move operation in the vulnerable version. This means that not only safe files can be uploaded, but it is also possible to upload files with a .php extension. Since no filename sanitization is utilized, the malicious parameter also facilitates path traversal, allowing the file to be moved even to the webroot directory. This makes it possible for unauthenticated attackers to upload arbitrary malicious PHP code and then access the file to trigger remote code execution on the server.

As with all arbitrary file upload vulnerabilities, this can lead to complete site compromise through the use of webshells and other techniques.

Wordfence Firewall

The following graphic demonstrates the steps to exploitation an attacker might take and at which point the Wordfence firewall would block an attacker from successfully exploiting the vulnerability.

Disclosure Timeline

January 8, 2026 – We received the submission for the Arbitrary File Upload vulnerability in Ninja Forms – File Upload via the Wordfence Bug Bounty Program.
January 8, 2026 – We validated the report and confirmed the proof-of-concept exploit.
January 8, 2026 – Full disclosure details were sent instantly to the vendor through our Wordfence Vulnerability Management Portal.
January 8, 2026Wordfence Premium, Care, and Response users received a firewall rule to provide added protection against any exploits that may target this vulnerability.
January 12, 2026 – The vendor acknowledged the report and began working on a fix.
January 27, 2026 – The vendor sent us the patch for review.
February 7, 2026 – Wordfence Free users received the same protection.
February 10, 2026 – The partially patched version of the plugin, 3.3.25, was released.
March 19, 2026 – The fully patched version of the plugin, 3.3.27, was released.

Conclusion

In this blog post, we detailed an Arbitrary File Upload vulnerability within the Ninja Forms – File Upload plugin affecting versions 3.3.26 and earlier. This vulnerability allows unauthenticated threat actors to execute malicious code on the server. The vulnerability has been fully addressed in version 3.3.27 of the plugin.

We encourage WordPress users to verify that their sites are updated to the latest patched version of Ninja Forms – File Upload as soon as possible considering the critical nature of this vulnerability.

Wordfence Premium, Wordfence Care, and Wordfence Response users received a firewall rule to protect against any exploits targeting this vulnerability on January 8, 2026. Sites using the free version of Wordfence received the same protection 30 days later on February 7, 2026.

If you know someone who uses this plugin on their site, we recommend sharing this advisory with them to ensure their site remains secure, as this vulnerability poses a significant risk.

The post 50,000 WordPress Sites affected by Arbitrary File Upload Vulnerability in Ninja Forms – File Upload WordPress Plugin appeared first on Wordfence.

Leave a Comment