SECURITY ADVISORY / 01

CVE-2025-2328 Exploit & Vulnerability Analysis

Complete CVE-2025-2328 security advisory with proof of concept (PoC), exploit details, and patch analysis for drag-and-drop-multiple-file-upload-contact-form-7.

drag-and-drop-multiple-file-upload-contact-form-7 products NVD ↗
Exploit PoC Vulnerability Patch Analysis

The Exploit

An unauthenticated attacker with the ability to submit a Contact Form 7 form (when Flamingo is active) can craft a malicious file path containing traversal sequences, then trigger deletion of arbitrary files on the server by having an Administrator view and delete the associated Flamingo inbound message.

POST /wp-json/contact-form-7/v1/contact_forms/<form_id>/feedback HTTP/1.1
Host: target.local
Content-Type: multipart/form-data; boundary=----WebKitBoundary

------WebKitBoundary
Content-Disposition: form-data; name="your-file"; filename="../../../../etc/passwd"
Content-Type: application/octet-stream

[file content]
------WebKitBoundary
Content-Disposition: form-data; name="_wpnonce"

[valid nonce]
------WebKitBoundary--

The attacker submits a form with a filename containing path traversal sequences. When an Administrator opens the corresponding Flamingo inbound message in wp-admin and clicks delete, the plugin's dnd_remove_uploaded_files() function extracts file paths from the post content without validating they stay within the uploads directory, then calls wp_delete_file() on the attacker-controlled path. The server deletes the target file (e.g., wp-config.php or a plugin containing a backdoor), and the Administrator receives no warning that a system file was destroyed.

What the Patch Did

Before:

foreach( $matches[0] as $files ) {
    $new_file = str_replace( site_url().'/', wp_normalize_path( ABSPATH ), $files );
    if( file_exists( $new_file ) ) {
        wp_delete_file( $new_file );
    }
}

After:

foreach ( $matches[0] as $files ) {
    // Convert url to dir
    $file = str_replace( site_url() . '/', wp_normalize_path( ABSPATH ), $files );

    // Check if it's a regular file.
    if ( is_file( $file ) ) {
        // Extract and sanitize the filename
        $file_path = dirname( $file ) . '/' . sanitize_file_name( wp_basename( $file ) );

        // Prevent traversal attack
        $real_path   = realpath( $file_path );
        $wp_dir      = wp_get_upload_dir();
        $uploads_dir = wp_normalize_path( realpath( $wp_dir['basedir'] ) . '/' . wpcf7_dnd_dir );

        // Check if the file exists and is within the uploads directory
        if ( $real_path && file_exists( $real_path ) && strpos( $real_path, $uploads_dir ) === 0 ) {
            wp_delete_file( $real_path );
        }
    }
}

The patch added directory confinement via realpath() and boundary validation. The fixed code resolves the canonical filesystem path of the target file using realpath(), obtains the canonical uploads directory path, then verifies with strpos( $real_path, $uploads_dir ) === 0 that the resolved file path is a child of the uploads directory. It also introduced sanitize_file_name() to strip dangerous characters and is_file() to ensure only regular files (not directories) can be deleted. Previously, any string after site_url() was treated as a valid file path.

Root Cause

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The post_content field of a Flamingo inbound message is user-controlled: an attacker submits a contact form with a crafted filename. The plugin uses preg_match_all() to extract file paths from that post content, then passes the matched path directly into wp_delete_file() after only a trivial str_replace() substitution. The attacker-controlled path crosses the trust boundary from user input to filesystem operation without validation that it stays within the intended uploads directory. A path like /wp-json/contact-form-7/v1/contact_forms/123/feedback?your-file=../../../../wp-config.php arrives in the form submission, gets stored in the Flamingo message, and is later extracted and deleted without boundary checking.

Why It Works

The load-bearing line is strpos( $real_path, $uploads_dir ) === 0. This check enforces that the resolved file path begins with the canonical uploads directory path; without it, ../../../../wp-config.php would still be deleted. The other additions (.realpath(), is_file(), sanitize_file_name()) are defense-in-depth: realpath() collapses .. sequences so that the boundary check is meaningful (the raw string ../../../../wp-config.php would not match the strpos() test, but after resolution it points to a real location that can be compared); is_file() prevents directory deletion; sanitize_file_name() blocks non-filesystem characters. If you removed the strpos() check alone, the bug is exploitable. If you removed only realpath(), path traversal sequences would persist in the string comparison and the check would fail to catch them.

Hardening Checklist

  • Use realpath() before any file operation on user-influenced paths, not just wp_normalize_path(). Normalization alone does not resolve .. to canonical form; realpath() does.
  • Implement a whitelist-based boundary check with string prefix matching (e.g., strpos( $real_path, $safe_dir ) === 0). Never rely on blacklisting dangerous characters; use positive allowance.
  • Call is_file() or is_dir() before deletion to confirm the type of filesystem object matches your intent. This prevents accidental deletion of directories or other special files.
  • Apply sanitize_file_name() to any user-provided filename before constructing a filesystem path. This strips null bytes, double-dot sequences, and path separators that might survive earlier normalization.
  • Never extract file paths from post content without re-validating them at deletion time. A path that was safe when stored may be unsafe if validation logic changes; re-check at the sink.

References

  • https://nvd.nist.gov/vuln/detail/CVE-2025-2328

Frequently asked questions about CVE-2025-2328

What is CVE-2025-2328?

CVE-2025-2328 is a security vulnerability identified in drag-and-drop-multiple-file-upload-contact-form-7. This security advisory provides detailed technical analysis of the vulnerability, exploit methodology, affected versions, and complete remediation guidance.

Is there a PoC (proof of concept) for CVE-2025-2328?

Yes. This writeup includes proof-of-concept details and a technical exploit breakdown for CVE-2025-2328. Review the analysis sections above for the PoC walkthrough and code examples.

How does CVE-2025-2328 get exploited?

The technical analysis section explains the vulnerability mechanics, attack vectors, and exploitation methodology affecting drag-and-drop-multiple-file-upload-contact-form-7. PatchLeaks publishes this information for defensive and educational purposes.

What products and versions are affected by CVE-2025-2328?

CVE-2025-2328 affects drag-and-drop-multiple-file-upload-contact-form-7. Check the affected-versions section of this advisory for specific version ranges, vulnerable configurations, and compatibility information.

How do I fix or patch CVE-2025-2328?

The patch analysis section provides guidance on updating to patched versions, applying workarounds, and implementing compensating controls for drag-and-drop-multiple-file-upload-contact-form-7.

What is the CVSS score for CVE-2025-2328?

The severity rating and CVSS scoring for CVE-2025-2328 affecting drag-and-drop-multiple-file-upload-contact-form-7 is documented in the vulnerability details section. Refer to the NVD entry for the current authoritative score.