Automated Threat Modeling

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 flaw in the Everest Forms WordPress plugin that lets unauthenticated attackers read and delete arbitrary files, with 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 lets any external visitor read sensitive server files such as wp-config.php with a crafted form submission. The plugin's own cleanup routine then deletes those files automatically. Over 100,000 active WordPress installations rely on this plugin for contact forms, payment forms, and quizzes, so the attack surface for CVE-2026-5478 is large.

Everest Forms, developed by wpeverest, is a popular drag-and-drop form builder for WordPress that supports file uploads, payment integrations, and email notifications. It is used across small-business and enterprise WordPress deployments, so a vulnerability in this plugin has wide impact. wpeverest distributes the plugin through the official WordPress plugin repository and maintains it in 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 has a CVSS score of 8.1. The problem is in class-evf-form-fields-upload.php, 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 trusts this attacker-controlled input without question:

// 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 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 this 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. It does no canonicalization (there is no realpath() call to resolve ../ sequences), and it does not enforce any directory boundary. An attacker can craft a URL whose path component contains traversal sequences that, after the substitution, resolve to any file on the filesystem.

Attack Flow

The exploitation chain runs like this:

  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 plugin attaches the resolved file to the outgoing notification email, which sends its contents to an email address the attacker controls.
  6. After the email is sent, the plugin's cleanup routine calls unlink() on the same resolved path, which permanently deletes the targeted file from the server.

Reading the file and then deleting it does double damage. The attacker gets the database credentials and authentication salts from wp-config.php, and deleting that file causes an immediate denial of service, because WordPress cannot run without it.

Prerequisites

Exploitation requires two conditions:

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

Site administrators choose both settings, so only a subset of Everest Forms deployments is vulnerable. However, the "disable entry storage" option is commonly used for GDPR compliance, which may make vulnerable configurations more common.

Patch Information

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

New Centralized Validation: resolve_uploads_file_from_url()

Version 3.4.5 adds a new method that replaces all of the 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 is the textbook defense against path traversal. The method canonicalizes the path with realpath() to collapse any ../ sequences, then checks that the resolved path is still inside the WordPress uploads base directory. It also rejects URLs that do not begin with the site's own uploads base URL, which blocks cross-origin file references entirely.

Validated old_files Handling in format()

The patched format() method no longer merges decoded old files without checking them. It iterates over each entry, validates the value string, runs it through resolve_uploads_file_from_url(), and keeps only the 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() used the vulnerable regex pattern. Version 3.4.5 refactors both 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 adds defense in depth. 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. Exploitation requires this specific configuration:

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

Version 3.4.5 and later contain the fix.

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

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization