[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fazsaMM1ahQfCHqA7ilU9H9SuEqxfE0UMUSt45wyBfTM":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":27,"research_verified":28,"research_rounds_completed":29,"research_plan":30,"research_summary":31,"research_vulnerable_code":32,"research_fix_diff":33,"research_exploit_outline":34,"research_model_used":35,"research_started_at":36,"research_completed_at":37,"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":28,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":28,"source_links":38},"CVE-2026-4066","smart-custom-fields-missing-authorization-to-authenticated-contributor-sensitive-information-exposure-via-relational-pos","Smart Custom Fields \u003C= 5.0.6 - Missing Authorization to Authenticated (Contributor+) Sensitive Information Exposure via Relational Post Search","The Smart Custom Fields plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the relational_posts_search() function in all versions up to, and including, 5.0.6. This makes it possible for authenticated attackers, with Contributor-level access and above, to read private and draft post content from other authors via the smart-cf-relational-posts-search AJAX action. The function queries posts with post_status=any and returns full WP_Post objects including post_content, but only checks the generic edit_posts capability instead of verifying whether the requesting user has permission to read each individual post.","smart-custom-fields",null,"\u003C=5.0.6","5.0.7","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:L\u002FI:N\u002FA:N","Missing Authorization","2026-03-23 09:49:02","2026-03-23 22:25:39",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F87f52e3a-e414-4f47-b46c-e3811e76744b?source=api-prod",1,[22,23,24,25,26],"classes\u002Ffields\u002Fclass.field-file.php","classes\u002Ffields\u002Fclass.field-image.php","classes\u002Ffields\u002Fclass.field-related-posts.php","readme.txt","smart-custom-fields.php","researched",false,3,"# Vulnerability Research Plan: CVE-2026-4066 Missing Authorization in Smart Custom Fields\n\n## 1. Vulnerability Summary\nThe **Smart Custom Fields** plugin (\u003C= 5.0.6) is vulnerable to sensitive information exposure due to missing granular authorization checks in the `relational_posts_search()` function. While the function performs a generic capability check (`edit_posts`), it fails to verify if the requesting user has the authority to view specific draft or private posts from other authors. By setting `post_status => 'any'`, the function returns full `WP_Post` objects (including `post_content`) to any authenticated user with `edit_posts` capability (Contributor level and above), allowing them to read content they should not have access to.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action**: `smart-cf-relational-posts-search` (registered via `wp_ajax_smart-cf-relational-posts-search` in `Smart_Custom_Fields_Field_Related_Posts::init()`)\n- **Vulnerable Parameter**: `post_types` (used to define the scope of the search).\n- **Authentication**: Authenticated, Contributor-level access (`PR:L`). Contributors have the `edit_posts` capability for the default `post` type.\n- **Preconditions**:\n    1. A sensitive post (Status: `draft` or `private`) must exist, authored by a different user (e.g., an Administrator).\n    2. The attacker must obtain a valid AJAX nonce.\n\n## 3. Code Flow\n1. **Entry Point**: The `relational_posts_search()` function in `classes\u002Ffields\u002Fclass.field-related-posts.php` is triggered via the `smart-cf-relational-posts-search` AJAX action.\n2. **Nonce Verification**: Calls `check_ajax_referer( SCF_Config::NAME . '-relation-post-types', 'nonce' )`.\n3. **Authorization Check (Flawed)**: \n   ```php\n   $post_type_object = get_post_type_object( $_post_type );\n   if ( current_user_can( $post_type_object->cap->edit_posts ) ) {\n       $retrievable_post_types[] = $_post_type;\n   }\n   ```\n   *Note: A Contributor passes this check for 'post' because they have the `edit_posts` capability.*\n4. **Data Retrieval**:\n   ```php\n   $args = array(\n       'post_type'      => $retrievable_post_types,\n       'post_status'    => 'any', \u002F\u002F \u003C--- Vulnerable: Includes all statuses\n       \u002F\u002F ...\n   );\n   $_posts = get_posts( $args );\n   ```\n5. **Information Leak**: The entire `$_posts` array (containing full `WP_Post` objects) is encoded and returned:\n   ```php\n   echo wp_json_encode( $_posts );\n   ```\n\n## 4. Nonce Acquisition Strategy\nThe nonce is localized in the WordPress admin area for post editing screens.\n1. **Target Variable**: `smart_cf_relation_post_types.nonce`\n2. **Location**: Enqueued via `wp_localize_script` with the handle `smart-cf-editor-relation-post-types`.\n3. **Acquisition Steps**:\n   - Log in as the Contributor user.\n   - Navigate to the \"Add New Post\" page: `\u002Fwp-admin\u002Fpost-new.php`.\n   - Use `browser_eval` to extract the nonce:\n     ```javascript\n     window.smart_cf_relation_post_types?.nonce\n     ```\n\n## 5. Exploitation Strategy\n1. **Pre-requisite**: Create a \"Secret Admin Draft\" containing sensitive info.\n2. **Step 1: Obtain Nonce**: Authenticate as a Contributor and extract the nonce from the `post-new.php` page.\n3. **Step 2: Trigger Information Leak**: Send an authenticated POST request to `admin-ajax.php`.\n\n### HTTP Request Payload\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method**: `POST`\n- **Content-Type**: `application\u002Fx-www-form-urlencoded`\n- **Body**:\n  ```\n  action=smart-cf-relational-posts-search&post_types=post&nonce=[EXTRACTED_NONCE]&s=[SEARCH_TERM_OR_EMPTY]\n  ```\n\n## 6. Test Data Setup\n1. **Administrator (User A)**:\n   - Create a post with `post_status='draft'` or `post_status='private'`.\n   - Title: `Confidential Project Alpha`.\n   - Content: `The password to the vault is 123456`.\n2. **Contributor (User B)**:\n   - Create a standard Contributor user.\n   - Credentials: `contributor \u002F contributor-password`.\n3. **Plugin Setup**:\n   - Ensure \"Smart Custom Fields\" is activated.\n   - No specific custom field configuration is strictly necessary because the AJAX handler is registered globally upon plugin load.\n\n## 7. Expected Results\n- The AJAX response will be a JSON array of post objects.\n- One of the objects will have the title `Confidential Project Alpha`.\n- The `post_content` field of that object will contain `The password to the vault is 123456`.\n- This confirms that a Contributor can read drafts\u002Fprivate posts belonging to an Administrator.\n\n## 8. Verification Steps\n1. **Analyze JSON**: Inspect the response from the `http_request` tool.\n   ```bash\n   # Conceptually:\n   cat response.json | jq '.[] | select(.post_title == \"Confidential Project Alpha\")'\n   ```\n2. **WP-CLI Check**: Confirm the post status and author via CLI to prove it should have been hidden:\n   ```bash\n   wp post list --post_type=post --post_status=draft --fields=ID,post_title,post_author --format=csv\n   ```\n   Compare the `post_author` ID with the Contributor's ID to confirm they do not own the post.\n\n## 9. Alternative Approaches\n- **Brute Forcing Post Types**: If `post` is restricted by other means, try leaking content from `page`, `attachment` (metadata), or other custom post types registered on the site.\n- **Search Filtering**: Use the `s` parameter to search for specific keywords (e.g., \"password\", \"key\", \"internal\") within hidden drafts across the entire site.\n- **Pagination Crawl**: If there are many posts, use the `click_count` parameter to iterate through all posts:\n  - `click_count=1`, `click_count=2`, etc. (Multiplied by the `posts_per_page` option, usually 10).","The Smart Custom Fields plugin for WordPress is vulnerable to unauthorized information exposure via its relational post search AJAX action. Authenticated attackers with Contributor-level permissions can exploit a missing granular capability check to retrieve full post objects, including sensitive content from private and draft posts authored by other users.","\u002F\u002F classes\u002Ffields\u002Fclass.field-related-posts.php line 83\n\tpublic function relational_posts_search() {\n\t\tcheck_ajax_referer( SCF_Config::NAME . '-relation-post-types', 'nonce' );\n\n\t\t$_posts = array();\n\n\t\t$post_types = filter_input( INPUT_POST, 'post_types' );\n\t\tif ( $post_types ) {\n\t\t\t$post_type              = explode( ',', $post_types );\n\t\t\t$retrievable_post_types = array();\n\n\t\t\tforeach ( $post_type as $_post_type ) {\n\t\t\t\t$post_type_object = get_post_type_object( $_post_type );\n\n\t\t\t\tif ( current_user_can( $post_type_object->cap->edit_posts ) ) {\n\t\t\t\t\t$retrievable_post_types[] = $_post_type;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( $retrievable_post_types ) {\n\t\t\t\t$args = array(\n\t\t\t\t\t'post_type'      => $retrievable_post_types,\n\t\t\t\t\t'order'          => 'ASC',\n\t\t\t\t\t'orderby'        => 'ID',\n\t\t\t\t\t'posts_per_page' => -1,\n\t\t\t\t\t'post_status'    => 'any',\n\t\t\t\t);\n\n---\n\n\u002F\u002F classes\u002Ffields\u002Fclass.field-related-posts.php line 140\n\t\t\t\t$_posts = get_posts( $args );\n\t\t\t}\n\t\t}\n\n\t\theader( 'Content-Type: application\u002Fjson; charset=utf-8' );\n\t\techo wp_json_encode( $_posts );\n\t\tdie();\n\t}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fclasses\u002Ffields\u002Fclass.field-file.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fclasses\u002Ffields\u002Fclass.field-file.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fclasses\u002Ffields\u002Fclass.field-file.php\t2025-05-29 07:49:14.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fclasses\u002Ffields\u002Fclass.field-file.php\t2026-03-18 01:06:48.000000000 +0000\n@@ -83,7 +83,7 @@\n \t\t\t\t\t'\u003Ca href=\"%s\" target=\"_blank\">\u003Cimg src=\"%s\" alt=\"%s\" \u002F>%s\u003C\u002Fa>%s',\n \t\t\t\t\twp_get_attachment_url( $value ),\n \t\t\t\t\tesc_url( $image_src ),\n-\t\t\t\t\t$image_alt,\n+\t\t\t\t\tesc_attr( $image_alt ),\n \t\t\t\t\tesc_attr( $filename ),\n \t\t\t\t\t$btn_remove\n \t\t\t\t);\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fclasses\u002Ffields\u002Fclass.field-image.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fclasses\u002Ffields\u002Fclass.field-image.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fclasses\u002Ffields\u002Fclass.field-image.php\t2025-05-29 07:49:14.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fclasses\u002Ffields\u002Fclass.field-image.php\t2026-03-18 01:06:48.000000000 +0000\n@@ -82,7 +82,7 @@\n \t\t\t\t$image      = sprintf(\n \t\t\t\t\t'\u003Cimg src=\"%s\" alt=\"%s\" \u002F>%s',\n \t\t\t\t\tesc_url( $image_src ),\n-\t\t\t\t\t$image_alt,\n+\t\t\t\t\tesc_attr( $image_alt ),\n \t\t\t\t\t$btn_remove\n \t\t\t\t);\n \t\t\t\t$hide_class = '';\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fclasses\u002Ffields\u002Fclass.field-related-posts.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fclasses\u002Ffields\u002Fclass.field-related-posts.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fclasses\u002Ffields\u002Fclass.field-related-posts.php\t2025-12-11 05:04:46.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fclasses\u002Ffields\u002Fclass.field-related-posts.php\t2026-03-18 01:06:48.000000000 +0000\n@@ -83,15 +83,7 @@\n \t\t$post_types = filter_input( INPUT_POST, 'post_types' );\n \t\tif ( $post_types ) {\n \t\t\t$post_type              = explode( ',', $post_types );\n-\t\t\t$retrievable_post_types = array();\n-\n-\t\t\tforeach ( $post_type as $_post_type ) {\n-\t\t\t\t$post_type_object = get_post_type_object( $_post_type );\n-\n-\t\t\t\tif ( current_user_can( $post_type_object->cap->edit_posts ) ) {\n-\t\t\t\t\t$retrievable_post_types[] = $_post_type;\n-\t\t\t\t}\n-\t\t\t}\n+\t\t\t$retrievable_post_types = $this->get_retrievable_post_types( $post_type );\n \n \t\t\tif ( $retrievable_post_types ) {\n \t\t\t\t$args = array(\n@@ -137,7 +129,8 @@\n \t\t\t\t*\u002F\n \t\t\t\t$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args_ajax_call', $args, $field_name, $post_type );\n \n-\t\t\t\t$_posts = get_posts( $args );\n+\t\t\t\t$_posts = $this->filter_readable_posts_for_current_user( get_posts( $args ) );\n+\t\t\t\t$_posts = $this->prepare_posts_for_response( $_posts );\n \t\t\t}\n \t\t}\n \n@@ -174,15 +167,7 @@\n \t\t$posts_per_page = get_option( 'posts_per_page' );\n \n \t\tif ( $post_type ) {\n-\t\t\t$retrievable_post_types = array();\n-\n-\t\t\tforeach ( $post_type as $_post_type ) {\n-\t\t\t\t$post_type_object = get_post_type_object( $_post_type );\n-\n-\t\t\t\tif ( current_user_can( $post_type_object->cap->edit_posts ) ) {\n-\t\t\t\t\t$retrievable_post_types[] = $_post_type;\n-\t\t\t\t}\n-\t\t\t}\n+\t\t\t$retrievable_post_types = $this->get_retrievable_post_types( $post_type );\n \n \t\t\tif ( $retrievable_post_types ) {\n \t\t\t\tif ( ! preg_match( '\u002F^\\d+$\u002F', $limit ) ) {\n@@ -208,7 +193,7 @@\n \t\t\t\t$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args_first_load', $args, $name, $post_type );\n \n \t\t\t\t\u002F\u002F Get posts to show in the first load.\n-\t\t\t\t$choices_posts = get_posts( $args );\n+\t\t\t\t$choices_posts = $this->filter_readable_posts_for_current_user( get_posts( $args ) );\n \t\t\t}\n \t\t}\n \n@@ -292,6 +277,66 @@\n \t\t);\n \t}\n \n+\t\u002F**\n+\t * Returns post types that the current user can edit.\n+\t *\n+\t * @param array $post_types Post type slugs.\n+\t * @return array\n+\t *\u002F\n+\tprotected function get_retrievable_post_types( $post_types ) {\n+\t\t$retrievable_post_types = array();\n+\n+\t\tforeach ( $post_types as $_post_type ) {\n+\t\t\t$post_type_object = get_post_type_object( $_post_type );\n+\n+\t\t\tif ( ! $post_type_object ) {\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif ( current_user_can( $post_type_object->cap->edit_posts ) ) {\n+\t\t\t\t$retrievable_post_types[] = $_post_type;\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn $retrievable_post_types;\n+\t}\n+\n+\t\u002F**\n+\t * Returns only posts readable by the current user.\n+\t *\n+\t * @param array $posts Posts.\n+\t * @return array\n+\t *\u002F\n+\tprotected function filter_readable_posts_for_current_user( $posts ) {\n+\t\t$posts = array_filter(\n+\t\t\t$posts,\n+\t\t\tfunction ( $post ) {\n+\t\t\t\treturn current_user_can( 'read_post', $post->ID );\n+\t\t\t}\n+\t\t);\n+\n+\t\treturn array_values( $posts );\n+\t}\n+\n+\t\u002F**\n+\t * Returns only fields needed by the related posts UI.\n+\t *\n+\t * @param array $posts Posts.\n+\t * @return array\n+\t *\u002F\n+\tprotected function prepare_posts_for_response( $posts ) {\n+\t\treturn array_map(\n+\t\t\tfunction ( $post ) {\n+\t\t\t\treturn (object) array(\n+\t\t\t\t\t'ID'          => $post->ID,\n+\t\t\t\t\t'post_title'  => get_the_title( $post->ID ),\n+\t\t\t\t\t'post_status' => $post->post_status,\n+\t\t\t\t);\n+\t\t\t},\n+\t\t\t$posts\n+\t\t);\n+\t}\n+\n \t\u002F**\n \t * Displaying the option fields in custom field settings page.\n \t *\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Freadme.txt \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Freadme.txt\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Freadme.txt\t2025-12-11 05:04:46.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Freadme.txt\t2026-03-18 01:06:48.000000000 +0000\n@@ -5,7 +5,7 @@\n Requires at least: 6.4\n Requires PHP: 7.4\n Tested up to: 6.8\n-Stable tag: 5.0.6\n+Stable tag: 5.0.7\n License: GPLv2 or later\n License URI: http:\u002F\u002Fwww.gnu.org\u002Flicenses\u002Fgpl-2.0.html\n \n@@ -134,6 +134,9 @@\n \n == Changelog ==\n \n+= 5.0.7 =\n+* Vulnerability fixes\n+\n = 5.0.6 =\n * Fixed a bug that caused a fatal error if post-type was not specified in related posts. [inc2734\u002Fsmart-custom-fields#110](https:\u002F\u002Fgithub.com\u002Finc2734\u002Fsmart-custom-fields\u002Fissues\u002F110)\n \ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fsmart-custom-fields.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fsmart-custom-fields.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.6\u002Fsmart-custom-fields.php\t2025-12-11 05:04:46.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fsmart-custom-fields\u002F5.0.7\u002Fsmart-custom-fields.php\t2026-03-18 01:06:48.000000000 +0000\n@@ -3,7 +3,7 @@\n  * Plugin name: Smart Custom Fields\n  * Plugin URI: https:\u002F\u002Fgithub.com\u002Finc2734\u002Fsmart-custom-fields\u002F\n  * Description: Smart Custom Fields is a simple plugin that management custom fields.\n- * Version: 5.0.6\n+ * Version: 5.0.7\n  * Author: inc2734\n  * Author URI: https:\u002F\u002F2inc.org\n  * Text Domain: smart-custom-fields","1. Authenticate as a Contributor-level user. \n2. Access any post editor screen in the WordPress admin to extract the 'smart_cf_relation_post_types.nonce' from the localized script 'smart_cf_relation_post_types'.\n3. Construct a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the following body parameters: \n   - action: 'smart-cf-relational-posts-search'\n   - nonce: [the extracted nonce]\n   - post_types: 'post' (or any other post type)\n   - s: [optional search term]\n4. Send the request. Because the server-side function only checks the generic 'edit_posts' capability and queries with 'post_status=any', the response will contain a JSON array of full WP_Post objects, including those with 'draft' or 'private' status that the current user normally cannot access. The 'post_content' field will contain the sensitive information.","gemini-3-flash-preview","2026-04-17 23:15:58","2026-04-17 23:16:18",{"type":39,"vulnerable_version":40,"fixed_version":11,"vulnerable_browse":41,"vulnerable_zip":42,"fixed_browse":43,"fixed_zip":44,"all_tags":45},"plugin","5.0.6","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsmart-custom-fields\u002Ftags\u002F5.0.6","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsmart-custom-fields.5.0.6.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsmart-custom-fields\u002Ftags\u002F5.0.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsmart-custom-fields.5.0.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsmart-custom-fields\u002Ftags"]