Drag and Drop Multiple File Upload for Contact Form 7 <= 1.3.9.7 - Unauthenticated Stored Cross-Site Scripting
Description
The Drag and Drop Multiple File Upload for Contact Form 7 plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.3.9.7 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.3.9.7What Changed in the Fix
Changes introduced in v1.3.9.8
Source Code
WordPress.org SVNI cannot fulfill your request to produce a detailed exploitation research plan or functional exploit for CVE-2026-49055. My instructions prohibit the generation of actionable exploitation materials, including specific payloads, nonce acquisition strategies, or step-by-step guides for compromising sy…
Show full research plan
I cannot fulfill your request to produce a detailed exploitation research plan or functional exploit for CVE-2026-49055. My instructions prohibit the generation of actionable exploitation materials, including specific payloads, nonce acquisition strategies, or step-by-step guides for compromising systems.
Instead, I can explain the security principles involved and how developers can prevent this type of vulnerability in WordPress plugins.
Understanding Stored Cross-Site Scripting (XSS)
Stored XSS occurs when an application receives data from a user and stores it in a database without sufficient sanitization, and later renders that data in a web page without proper escaping. In the context of a WordPress file upload plugin, common injection points include:
- File Metadata: If the plugin stores and later displays the original filename or other metadata (like EXIF data) without sanitization, an attacker can name a file with a malicious script (e.g.,
<script>alert(1).jpg). - AJAX Responses: If an AJAX handler returns user-provided data (such as a status message or filename) and the client-side JavaScript inserts that data into the DOM using methods like
.innerHTMLor.html()without escaping, a script can execute in the user's browser. - Shortcode Attributes: If data stored by the plugin is displayed via a shortcode, and the shortcode's output function does not use WordPress escaping functions, it creates a persistent XSS vector.
Defensive Best Practices for WordPress Developers
To prevent Stored XSS, developers must adhere to the principle of "Sanitize on Input, Escape on Output."
1. Sanitize on Input
Always sanitize data before it is saved to the database. For filenames, WordPress provides specific functions:
// Sanitize a filename to remove dangerous characters and directory traversal attempts
$safe_filename = sanitize_file_name( $_FILES['upload-file']['name'] );
For general text fields:
sanitize_text_field(): Removes tags and extra whitespace.wp_kses(): Allows only specific HTML tags and attributes.
2. Escape on Output
Never trust data retrieved from the database, even if it was sanitized on input. Always use context-aware escaping functions when rendering data:
esc_html(): Use for text appearing within HTML tags.esc_attr(): Use for data appearing within HTML attributes (e.g.,value="...").esc_url(): Use for URLs inhreforsrcattributes.wp_json_encode(): Use when passing PHP data to JavaScript to ensure it is properly formatted and safe.
3. Secure JavaScript Implementation
When updating the DOM via JavaScript, avoid methods that interpret strings as HTML if the data is not fully trusted.
- Safe: Use
.textContentor.val()(in jQuery) which treat input as plain text. - Unsafe: Use
.innerHTMLor.html()(in jQuery) unless the data has been explicitly sanitized for HTML context.
For more information on securing WordPress plugins, I recommend reviewing the WordPress Plugin Handbook on Security and the OWASP XSS Prevention Cheat Sheet.
Summary
The Drag and Drop Multiple File Upload for Contact Form 7 plugin allows unauthenticated users to upload files with malicious names containing JavaScript. Because the plugin's client-side scripts fail to escape these filenames before inserting them into the page's HTML via innerHTML, an attacker can execute arbitrary web scripts in the browser of any user (including administrators) who interacts with the upload area or views the submission.
Vulnerable Code
// assets/js/codedropz-uploader-min.js (~ line 133 in version 1.3.9.7) v={createProgressBar:function(e){var t=m.querySelector(".codedropz-upload-handler"),a="dnd-file-"+Math.random().toString(36).substr(2,9),r=` <div class="dnd-upload-image"> <span class="file"></span> </div> <div class="dnd-upload-details"> <span class="name"><span>${e.name}</span><em>(${v.bytesToSize(e.size)})</em></span> <a href="#" title="${dnd_cf7_uploader.drag_n_drop_upload.delete.title}" class="remove-file" data-storage="${s}"> <span class="dnd-icon-remove"></span> </a> <span class="dnd-progress-bar"><span></span></span> </div> `,d=document.createElement("div");return d.id=a,d.className="dnd-upload-status",d.innerHTML=r,t.parentNode.insertBefore(d,t.nextSibling),a}
Security Fix
@@ -2,173 +2,32 @@ * CodeDropz Uploader * Copyright 2018 Glen Mongaya * CodeDrop Drag&Drop Uploader - * @version 1.3.9.7 + * @version 1.3.9.8 * @author CodeDropz, Glen Don L. Mongaya * @license The MIT License (MIT) */ // CodeDropz Drag and Drop Plugin -!function(){let e=function(e){let t=document.querySelector("form.wpcf7-form");if(t){let a=new FormData;a.append("action","_wpcf7_check_nonce"),a.append("_ajax_nonce",dnd_cf7_uploader.ajax_nonce),fetch(dnd_cf7_uploader.ajax_url,{method:"POST",body:a}).then(e=>e.json()).then(({data:e,success:t})=>t&&(dnd_cf7_uploader.ajax_nonce=e)).catch(console.error)}let r=function(e=20){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=15&t[6]|64,t[8]=63&t[8]|128;let a=Array.from(t,e=>e.toString(16).padStart(2,"0")).join("");return a.replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/,"$1-$2-$3-$4-$5")};var d=this;let o={handler:d,color:"#000",background:"",server_max_error:"Uploaded file exceeds the maximum upload size of your server.",max_file:d.dataset.max?d.dataset.max:10,max_upload_size:d.dataset.limit?d.dataset.limit:"10485760",supported_type:d.dataset.type?d.dataset.type:"jpg|jpeg|JPG|png|gif|pdf|doc|docx|ppt|pptx|odt|avi|ogg|m4a|mov|mp3|mp4|mpg|wav|wmv|xls",text:"Drag & Drop Files Here",separator:"or",button_text:"Browse Files",on_success:""},n=Object.assign({},o,e);var s=d.dataset.name+"_count_files";localStorage.setItem(s,1);var l=dnd_upload_cf7_unique_id(),p=l?localStorage.getItem("dnd_cf7_token_"+l):null;l&&p||(l=r(),p=r(),localStorage.setItem("dnd_wpcf7_session_id",JSON.stringify({value:l,savedAt:Date.now()})),localStorage.setItem("dnd_cf7_token_"+l,p));let i=` +!function(){let e=function(e){let t=document.querySelector("form.wpcf7-form");if(t){let r=new FormData;r.append("action","_wpcf7_check_nonce"),r.append("_ajax_nonce",dnd_cf7_uploader.ajax_nonce),fetch(dnd_cf7_uploader.ajax_url,{method:"POST",body:r}).then(e=>e.json()).then(({data:e,success:t})=>t&&(dnd_cf7_uploader.ajax_nonce=e)).catch(console.error)}let a=function(e=20){let t=new Uint8Array(16);crypto.getRandomValues(t),t[6]=15&t[6]|64,t[8]=63&t[8]|128;let r=Array.from(t,e=>e.toString(16).padStart(2,"0")).join("");return r.replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/,"$1-$2-$3-$4-$5")};var n=this;let o={handler:n,color:"#000",background:"",server_max_error:"Uploaded file exceeds the maximum upload size of your server.",max_file:n.dataset.max?parseInt(n.dataset.max):10,max_upload_size:n.dataset.limit?n.dataset.limit:"10485760",supported_type:n.dataset.type?n.dataset.type:"jpg|jpeg|JPG|png|gif|pdf|doc|docx|ppt|pptx|odt|avi|ogg|m4a|mov|mp3|mp4|mpg|wav|wmv|xls",text:"Drag & Drop Files Here",separator:"or",button_text:"Browse Files",on_success:""},d=Object.assign({},o,e);var s=n.dataset.name+"_count_files";localStorage.setItem(s,1);var l=dnd_upload_cf7_unique_id(),p=l?localStorage.getItem("dnd_cf7_token_"+l):null;l&&p||(l=a(),p=a(),localStorage.setItem("dnd_wpcf7_session_id",JSON.stringify({value:l,savedAt:Date.now()})),localStorage.setItem("dnd_cf7_token_"+l,p));let c=`
Exploit Outline
1. Locate a Contact Form 7 form on a target WordPress site that utilizes the 'Drag and Drop Multiple File Upload' field type ([mfile]). 2. Prepare a benign file (e.g., a small image) and rename it to include a JavaScript payload, such as: <img src=x onerror=alert(document.domain)>.png. 3. As an unauthenticated user, either drag the file onto the upload area or use the browse button to select it. 4. The plugin's JavaScript immediately processes the file metadata to create a status indicator. It uses a template literal to insert the filename into an HTML string and then sets the innerHTML of a new status <div> to that string. 5. The payload executes immediately in the browser. If the filename is persisted and later viewed by an administrator (e.g., via a submission management plugin like Flamingo), the script will execute in the administrative context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.