[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fE01wc7S7brZvRCI445wPEnkzxQ3goNLRlHYJhaGSf6Y":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-32562","ppwp-password-protect-pages-missing-authorization","PPWP – Password Protect Pages \u003C= 1.9.15 - Missing Authorization","The PPWP – Password Protect Pages plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 1.9.15. This makes it possible for authenticated attackers, with Contributor-level access and above, to perform an unauthorized action.","password-protect-page",null,"\u003C=1.9.15","1.9.16","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-03-23 00:00:00","2026-04-02 15:05:31",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fac6f78a9-8fb0-4ea0-b02e-a0748723dca8?source=api-prod",11,[22,23,24,25,26,27,28,29],"README.txt","admin\u002Fclass-ppw-admin.php","admin\u002Findex.php","config.php","core\u002Fbase\u002Fclass-ppw-background-task.php","core\u002Fbase\u002Fclass-ppw-data-migration-manager.php","core\u002Fclass-ppw-functions.php","includes\u002Faddons\u002Fbeaver-builder\u002Fclass-ppw-beaver-loader.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-32562\n\n## 1. Vulnerability Summary\nThe **PPWP – Password Protect Pages** plugin (versions \u003C= 1.9.15) contains a missing authorization vulnerability in its password-setting functionality. The function `PPW_Admin::ppw_free_set_password` (located in `admin\u002Fclass-ppw-admin.php`) is responsible for saving password protection settings for posts and pages. While it performs a nonce check (via the helper `ppw_free_error_before_create_password`), it fails to verify if the current user has the authority to edit the post specified in the request or the capability to manage plugin settings. This allows an authenticated attacker with Contributor-level permissions to modify password protection settings for any post, including those authored by Administrators.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `ppw_set_password` (inferred from function name and common plugin patterns)\n- **HTTP Method:** `POST`\n- **Authentication:** Required (Contributor-level or higher)\n- **Vulnerable Parameter:** `settings[id_page_post]` (allows specifying any Post ID)\n- **Payload Parameters:**\n    - `action`: `ppw_set_password`\n    - `nonce`: A valid security nonce (retrieved from the post metabox)\n    - `settings[save_password]`: The new password to set for the post.\n    - `settings[id_page_post]`: The target Post ID (e.g., an Administrator's post).\n    - `settings[is_role_selected]`: Boolean\u002FString indicating if role-based protection is active.\n    - `settings[ppwp_multiple_password][]`: Array of passwords (if using multiple).\n\n## 3. Code Flow\n1. **Entry Point:** A logged-in user sends a request to `admin-ajax.php` with the action `ppw_set_password`.\n2. **Hook Registration:** The plugin registers `ppw_free_set_password` as an AJAX handler (typically in the main plugin file or a loader, though not shown in the snippet, the naming convention in `PPW_Admin` confirms its purpose).\n3. **Nonce Validation:** `ppw_free_set_password` calls `ppw_free_error_before_create_password($_REQUEST, $setting_keys)`. This function verifies the nonce provided in the request.\n4. **Missing Check:** Immediately following the nonce check, the function proceeds to extract `$data_settings` from `$_REQUEST['settings']` and calls `$free_services->create_new_password()`.\n5. **Execution:** The `create_new_password` method updates the post meta (storing the password) for the ID provided in `id_page_post` without checking if the current user has `edit_post` capabilities for that specific ID.\n\n## 4. Nonce Acquisition Strategy\nThe nonce is required to pass the `ppw_free_error_before_create_password` check. The plugin enqueues a metabox on the post edit screen where this nonce is generated.\n\n1. **Access:** A Contributor can access the WordPress dashboard and navigate to `wp-admin\u002Fpost-new.php` or edit one of their own posts.\n2. **Shortcode\u002FMetabox:** The metabox logic is defined in `ppw_free_add_custom_meta_box_to_edit_page` (includes `view-ppw-meta-box.php`).\n3. **Extraction:**\n    - Use `browser_navigate` to `wp-admin\u002Fpost-new.php`.\n    - Use `browser_eval` to extract the nonce.\n    - **JS Variable:** The plugin often localizes data into an object named `ppw_free_admin` or `ppwp_data`. \n    - **Metabox Field:** Alternatively, the nonce is likely in a hidden input field within the `ppw-meta-box` div.\n    - **Command:** `browser_eval(\"document.querySelector('#ppw_nonce')?.value || document.querySelector('input[name*=\\\"nonce\\\"]')?.value\")`.\n\n## 5. Exploitation Strategy\n1. **Login:** Authenticate as a Contributor user.\n2. **Nonce Retrieval:**\n    - Navigate to `wp-admin\u002Fpost-new.php`.\n    - Extract the nonce value associated with the PPWP metabox.\n3. **Target Identification:** Identify the ID of a published Administrator post (e.g., Post ID 1).\n4. **Malicious Request:**\n    - Send a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php`.\n    - **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`.\n    - **Body:**\n      ```\n      action=ppw_set_password&nonce=[NONCE]&settings[id_page_post]=[TARGET_ID]&settings[save_password]=pwned123&settings[is_role_selected]=0&settings[ppwp_multiple_password][]=pwned123\n      ```\n5. **Impact:** The Administrator's post is now password-protected with \"pwned123\", potentially locking out legitimate visitors or altering the site's intended visibility.\n\n## 6. Test Data Setup\n1. **Admin Post:** Create a post as the Administrator: `wp post create --post_type=post --post_title=\"Admin Secret\" --post_status=publish --post_author=1` (Note the returned ID, e.g., `5`).\n2. **Contributor User:** Create a Contributor user: `wp user create attacker attacker@example.com --role=contributor --user_pass=password`.\n3. **Plugin Activation:** Ensure `password-protect-page` is active.\n\n## 7. Expected Results\n- The AJAX request should return a JSON response (generated by `wp_send_json( $current_roles_password )`) containing the updated password details.\n- The HTTP response status should be `200 OK`.\n- The target post should now be inaccessible without the password \"pwned123\".\n\n## 8. Verification Steps\n1. **Check Post Meta:** Use WP-CLI to verify the password was set on the Admin's post:\n   `wp post generate --post_id=[TARGET_ID] --format=json` \n   Or specifically check the meta keys:\n   `wp post meta list [TARGET_ID]` (Look for keys starting with `_ppw_` or `_pcp_`).\n2. **Verify Protection:** Attempt to access the post URL unauthenticated via `http_request`. The response should contain the PPWP password form (look for string `ppw_password` or `Password Protected`).\n\n## 9. Alternative Approaches\n- **Role-Based Bypass:** If `is_role_selected` is manipulated, the attacker might whitelist specific roles (like \"contributor\") to access admin-only content if the plugin uses these settings to override default WordPress permissions.\n- **Multiple Passwords:** Attempt to inject multiple passwords into the `ppwp_multiple_password` array to see if it allows overriding complex protection schemes.\n- **Action Guessing:** If `ppw_set_password` fails, check the page source for `wp_localize_script` to find the exact `action` string used by the plugin's `admin.js`. Common variants: `ppw_free_set_password`, `ppwp_save_settings`.","The PPWP – Password Protect Pages plugin for WordPress is vulnerable to unauthorized access because the AJAX function ppw_free_set_password fails to perform a capability check. This allows authenticated attackers with Contributor-level permissions or higher to modify the password protection settings of any post, including those authored by administrators.","\u002F\u002F admin\u002Fclass-ppw-admin.php line 143\n\tpublic function ppw_free_set_password() {\n\t\t$setting_keys = array( 'save_password', 'id_page_post', 'is_role_selected', 'ppwp_multiple_password' );\n\t\tif ( ppw_free_error_before_create_password( $_REQUEST, $setting_keys ) ) {  \u002F\u002F phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We handle nonce verification in this function\n\t\t\twp_send_json(\n\t\t\t\tarray(\n\t\t\t\t\t'is_error' => true,\n\t\t\t\t\t'message'  => PPW_Constants::BAD_REQUEST_MESSAGE,\n\t\t\t\t),\n\t\t\t\t400\n\t\t\t);\n\t\t\twp_die();\n\t\t}\n\t\tif ( ! isset( $_REQUEST['settings'] ) ) { \u002F\u002F phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We handle nonce verification above.\n\t\t\t\u002F\u002F ... (Truncated)\n\t\t}\n\n\t\t$data_settings        = $_REQUEST['settings']; \u002F\u002F phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We no need to verify nonce for enqueue assets, Don't need use wp_unslash(), and no need to sanitize settings params.\n\t\t$new_role_password    = $data_settings['save_password'];\n\t\t$id                   = $data_settings['id_page_post'];\n\t\t$role_selected        = $data_settings['is_role_selected'];\n\t\t$new_global_passwords = is_array( $data_settings['ppwp_multiple_password'] ) ? $data_settings['ppwp_multiple_password'] : array();\n\n\t\t$free_services          = new PPW_Password_Services();\n\t\t$current_roles_password = $free_services->create_new_password( $id, $role_selected, $new_global_passwords, $new_role_password );\n\t\twp_send_json( $current_roles_password );\n\t\twp_die();\n\t}","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpassword-protect-page\u002F1.9.15\u002Fadmin\u002Fclass-ppw-admin.php\t2025-07-17 07:02:26.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpassword-protect-page\u002F1.9.16\u002Fadmin\u002Fclass-ppw-admin.php\t2026-03-19 11:27:32.000000000 +0000\n@@ -171,6 +171,19 @@\n \t\t$id                   = $data_settings['id_page_post'];\n \t\t$role_selected        = $data_settings['is_role_selected'];\n \t\t$new_global_passwords = is_array( $data_settings['ppwp_multiple_password'] ) ? $data_settings['ppwp_multiple_password'] : array();\n+\n+\t\tif ( ! current_user_can( 'edit_post', $id ) ) {\n+\t\t\twp_send_json(\n+\t\t\t\tarray(\n+\t\t\t\t\t'is_error' => true,\n+\t\t\t\t\t'message'  => PPW_Constants::BAD_REQUEST_MESSAGE,\n+\t\t\t\t),\n+\t\t\t\t400\n+\t\t\t);\n+\n+\t\t\twp_die();\n+\t\t}\n+\n \t\t$free_services          = new PPW_Password_Services();\n \t\t$current_roles_password = $free_services->create_new_password( $id, $role_selected, $new_global_passwords, $new_role_password );\n \t\twp_send_json( $current_roles_password );","To exploit this vulnerability, an authenticated attacker with Contributor-level access needs to perform the following steps: \n1. Login to the WordPress dashboard as a Contributor. \n2. Navigate to a post editing screen (like `wp-admin\u002Fpost-new.php`) to retrieve a valid security nonce from the PPWP metabox (the plugin uses the same nonce logic for all posts).\n3. Identify the target Post ID that the attacker wishes to lock or modify.\n4. Send a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the `action` parameter set to `ppw_set_password`. \n5. In the `settings` array parameter, specify the target Post ID in `id_page_post` and the desired password in `save_password`. \n6. The plugin will process the request and update the post meta associated with the target ID because it only verifies the nonce and not the user's authority to edit that specific post.","gemini-3-flash-preview","2026-04-18 00:14:43","2026-04-18 00:15:13",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","1.9.15","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpassword-protect-page\u002Ftags\u002F1.9.15","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpassword-protect-page.1.9.15.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpassword-protect-page\u002Ftags\u002F1.9.16","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpassword-protect-page.1.9.16.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpassword-protect-page\u002Ftags"]