Everest Forms CVE-2026-5478: Brief Summary of Unauthenticated File Read and Deletion via Path Traversal

A brief summary of CVE-2026-5478, a path traversal vulnerability in the Everest Forms WordPress plugin that allows unauthenticated attackers to read and delete arbitrary files. Includes patch analysis and mitigation guidance.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-20

Everest Forms CVE-2026-5478: Brief Summary of Unauthenticated File Read and Deletion via Path Traversal
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

An unauthenticated path traversal flaw in the Everest Forms WordPress plugin allows any external visitor to read sensitive server files like wp-config.php through a crafted form submission, and then have those files automatically deleted by the plugin's own cleanup routine. With over 100,000 active WordPress installations relying on this plugin for contact forms, payment forms, and quizzes, the attack surface for CVE-2026-5478 is considerable.

Everest Forms, developed by wpeverest, is a popular drag and drop form builder for WordPress that supports file uploads, payment integrations, and email notifications. Its broad adoption across small business and enterprise WordPress deployments makes vulnerabilities in this plugin particularly impactful. The plugin is distributed through the official WordPress plugin repository and maintained via a public GitHub repository.

Technical Information

Root Cause: Untrusted Input in the old_files Parameter

The vulnerability is classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) and carries a CVSS score of 8.1. The core issue resides in the class-evf-form-fields-upload.php file, specifically in how the plugin handles the old_files parameter during public form submissions.

When a form includes a file upload or image upload field and is configured to disable storing entry information, the plugin processes previously uploaded file references through the old_files field. In version 3.4.4, the format() method blindly trusts this attacker controlled input:

// Version 3.4.4 - Vulnerable old_files handling in format() if ( isset( $field_submit['old_files'] ) ) { $old_data = array_map( function ( $file ) { $decoded = json_decode( $file, true ); return is_array( $decoded ) ? $decoded : array(); }, $field_submit['old_files'] ); $data = array_merge( $data, $old_data ); }

There is no validation whatsoever on the decoded file entries. The plugin simply merges whatever the attacker provides into the processed data array.

Insecure Path Resolution via Regex

The second critical flaw is in how the plugin converts these URL values into local filesystem paths. Methods like attach_entry_files_upload() used the following pattern:

$uploaded_file = ABSPATH . preg_replace( '/.*wp-content/', 'wp-content', wp_parse_url( $file_url, PHP_URL_PATH ) );

This regex based substitution strips everything before wp-content in the URL path and prepends ABSPATH. Critically, it performs no canonicalization (no realpath() call to resolve ../ sequences) and no directory boundary enforcement. An attacker can craft a URL whose path component contains traversal sequences that, after the regex substitution, resolve to any file on the filesystem.

Attack Flow

The exploitation chain proceeds as follows:

  1. The attacker identifies a WordPress site running Everest Forms with a form that contains a file upload field and has entry storage disabled.
  2. The attacker submits the form with a crafted old_files parameter containing a JSON encoded entry whose value field points to a URL with an embedded path traversal payload targeting, for example, wp-config.php.
  3. The plugin's format() method JSON decodes the payload and merges it into the processed data without validation.
  4. When the plugin prepares notification emails, it resolves the attacker supplied URL into a local filesystem path using the insecure regex substitution. The traversal payload resolves to the target file (e.g., wp-config.php).
  5. The resolved file is attached to the outgoing notification email, effectively exfiltrating its contents to the attacker's controlled email address.
  6. After the email is sent, the plugin's cleanup routine calls unlink() on the same resolved path, permanently deleting the targeted file from the server.

This dual action of exfiltration followed by destruction is particularly damaging. The attacker obtains database credentials and authentication salts from wp-config.php, and the subsequent deletion of that file causes an immediate denial of service as WordPress cannot function without it.

Prerequisites

Two conditions must be met for successful exploitation:

  • The targeted form must contain a file upload or image upload field.
  • The form must be configured to disable storing entry information.

Both conditions are configuration choices that site administrators make, which limits the vulnerable surface to a subset of Everest Forms deployments. However, the "disable entry storage" option is commonly used for GDPR compliance, which may increase the prevalence of vulnerable configurations.

Patch Information

The vulnerability was patched in Everest Forms version 3.4.5, released on April 20, 2026, via WordPress plugin repository changeset 3507814. The fix is concentrated in class-evf-form-fields-upload.php and introduces a fundamentally new approach to path resolution.

New Centralized Validation: resolve_uploads_file_from_url()

Version 3.4.5 adds an entirely new method that replaces all ad hoc regex path resolution with strict security checks:

// Version 3.4.5 - New secure path resolution method protected function resolve_uploads_file_from_url( $file_url ) { if ( empty( $file_url ) || ! is_string( $file_url ) ) { return false; } $file_url = esc_url_raw( $file_url ); $upload_dir = wp_get_upload_dir(); $uploads_baseurl = trailingslashit( $upload_dir['baseurl'] ); $uploads_basedir = wp_normalize_path( trailingslashit( $upload_dir['basedir'] ) ); // Reject URLs not starting with this site's uploads base URL if ( 0 !== strpos( $file_url, $uploads_baseurl ) ) { return false; } // Parse and compare URL path components $path = wp_parse_url( $file_url, PHP_URL_PATH ); $base_path = wp_parse_url( $uploads_baseurl, PHP_URL_PATH ); if ( ! is_string( $path ) || ! is_string( $base_path ) || 0 !== strpos( $path, $base_path ) ) { return false; } // Extract the relative path and build a candidate $relative_path = ltrim( substr( $path, strlen( $base_path ) ), '/' ); $candidate = wp_normalize_path( $uploads_basedir . $relative_path ); // Canonicalize with realpath() — the key defense against traversal $resolved_path = realpath( $candidate ); if ( false === $resolved_path ) { return false; } $resolved_path = wp_normalize_path( $resolved_path ); // Final boundary check: must still be inside uploads and be a file if ( 0 !== strpos( $resolved_path, $uploads_basedir ) || ! is_file( $resolved_path ) ) { return false; } return $resolved_path; }

This method implements the textbook defense against path traversal: canonicalize with realpath() to collapse any ../ sequences, then verify the resolved path still falls within the WordPress uploads base directory. It also rejects URLs that do not begin with the site's own uploads base URL, preventing cross origin file references entirely.

Validated old_files Handling in format()

The patched format() method no longer blindly merges decoded old files. It iterates each entry, validates the value string, runs it through resolve_uploads_file_from_url(), and only keeps entries that pass:

// Version 3.4.5 - Validated old_files handling in format() if ( isset( $field_submit['old_files'] ) && is_array( $field_submit['old_files'] ) ) { $validated_old_data = array(); foreach ( $field_submit['old_files'] as $file ) { $decoded = json_decode( $file, true ); if ( ! is_array( $decoded ) || empty( $decoded['value'] ) || ! is_string( $decoded['value'] ) ) { continue; } $resolved_path = $this->resolve_uploads_file_from_url( $decoded['value'] ); if ( false === $resolved_path ) { continue; } $decoded['value'] = esc_url_raw( $decoded['value'] ); $validated_old_data[] = $decoded; } if ( ! empty( $validated_old_data ) ) { $data = array_merge( $data, $validated_old_data ); } }

Replacement of Insecure Regex in Attachment Methods

Both attach_entry_files() and attach_entry_files_upload() previously used the vulnerable regex pattern. In version 3.4.5, both are refactored to use resolve_uploads_file_from_url():

// Old (vulnerable): $uploaded_file = ABSPATH . preg_replace( '/.*wp-content/', 'wp-content', wp_parse_url( $file, PHP_URL_PATH ) ); // New (patched): $resolved = $this->resolve_uploads_file_from_url( $file ); if ( false !== $resolved && ! in_array( $resolved, $entry_files, true ) ) { $entry_files[] = $resolved; }

This defense in depth approach ensures that even if tainted data somehow reached the attachment or cleanup phases, it would not resolve to an arbitrary filesystem path.

Affected Systems and Versions

All versions of the Everest Forms plugin for WordPress up to and including version 3.4.4 are vulnerable. The vulnerability requires the following specific configuration:

  • A form must contain a file upload or image upload field.
  • The form must have "storing entry information" disabled.

The fix is available in version 3.4.5 and later.

ConfigurationVulnerable?
Everest Forms <= 3.4.4 with upload field and entry storage disabledYes
Everest Forms <= 3.4.4 with upload field and entry storage enabledNo
Everest Forms <= 3.4.4 without upload fieldsNo
Everest Forms >= 3.4.5 (any configuration)No

References

Detect & fix
what others miss

Security magnifying glass visualization