[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fWlF6r5q1e6A6sPBiVlGVSQIokvFLvJYql4B6VJf0foU":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-6206","mw-wp-form-insecure-direct-object-reference-to-unauthenticated-sensitive-information-disclosure-via-postid-query-paramet","MW WP Form \u003C= 5.1.2 - Insecure Direct Object Reference to Unauthenticated Sensitive Information Disclosure via 'post_id' Query Parameter","The MW WP Form plugin for WordPress is vulnerable to Information Exposure in all versions up to, and including, 5.1.2 via the _get_post_property_from_querystring() function due to insufficient restrictions on which posts can be included. This makes it possible for unauthenticated attackers to extract data from password protected, private, or draft posts that they should not have access to.","mw-wp-form",null,"\u003C=5.1.2","5.1.3","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:L\u002FI:N\u002FA:N","Authorization Bypass Through User-Controlled Key","2026-05-13 19:51:32","2026-05-14 08:24:28",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F7f2c39f6-3d37-4765-99e8-023610856b61?source=api-prod",1,[22,23,24,25,26,27,28],"classes\u002Fabstract\u002Fclass.form-field.php","classes\u002Fabstract\u002Fclass.validation-rule.php","classes\u002Fmodels\u002Fclass.csrf.php","classes\u002Fmodels\u002Fclass.parser.php","classes\u002Fmodels\u002Fclass.session.php","classes\u002Fvalidation-rules\u002Fclass.maximagesize.php","mw-wp-form.php","researched",false,3,"This research plan outlines the technical analysis and proof-of-concept (PoC) steps for CVE-2026-6206, an Insecure Direct Object Reference (IDOR) vulnerability in the MW WP Form plugin.\n\n---\n\n### 1. Vulnerability Summary\nThe MW WP Form plugin suffers from an Information Exposure vulnerability due to an insecurely implemented feature that allows pre-filling form data from URL parameters. The function `_get_post_property_from_querystring()` in `classes\u002Fmodels\u002Fclass.parser.php` retrieves a post object based on a user-supplied `post_id` query parameter and extracts its properties or meta data without verifying the post's status (e.g., draft, private, or password-protected) or the current user's authorization to view that post.\n\n### 2. Attack Vector Analysis\n*   **Vulnerable Endpoint**: Any public-facing page or post where an MW WP Form shortcode is embedded, provided the form has the \"querystring\" setting enabled.\n*   **Action**: A `GET` request to the page containing the form.\n*   **Vulnerable Parameter**: `post_id` (Query String).\n*   **Required Authentication**: None (Unauthenticated).\n*   **Preconditions**: \n    1.  The form must have the \"URL parameter\" (internal key: `querystring`) setting enabled.\n    2.  The form content must contain placeholders using the curly brace syntax, such as `{post_title}`, `{post_content}`, or `{any_meta_key}`.\n\n### 3. Code Flow\n1.  **Entry Point**: A user visits a page containing the shortcode `[mwform_formkey key=\"...\"]`.\n2.  **Controller**: The shortcode triggers the form rendering process. The `MW_WP_Form_Parser` is instantiated to handle placeholder replacement.\n3.  **Parsing**: The method `MW_WP_Form_Parser::replace_for_page()` is called to process the form's HTML content.\n4.  **Property Replacement**: `replace_for_page()` calls `_replace_post_property()`.\n5.  **Setting Check**: `_replace_post_property()` checks if the setting `querystring` is truthy:\n    ```php\n    if ( $this->Setting->get( 'querystring' ) ) {\n        $callback = array( $this, '_get_post_property_from_querystring' );\n    }\n    ```\n6.  **IDOR Sink**: `_get_post_property_from_querystring()` is executed. It retrieves the post ID from `$_GET['post_id']` and calls `get_post()`:\n    ```php\n    $post = get_post( $_GET['post_id'] );\n    ```\n7.  **Disclosure**: The parser then calls `_get_post_property($post, $meta_key)`, which returns the requested property (like `post_content`) or calls `get_post_meta()` for the object. No checks for `post_status` or visibility are performed.\n\n### 4. Nonce Acquisition Strategy\nThis vulnerability occurs during the **rendering** of the form (Read operation), not during form submission (Write operation). \n*   The `replace_for_page()` method is called when the WordPress shortcode is expanded into HTML for display.\n*   **No nonce is required** to trigger this disclosure, as the `post_id` is processed via a standard `GET` request to a public page.\n\n### 5. Test Data Setup\nTo reproduce the vulnerability, the environment must be configured as follows:\n\n1.  **Create Sensitive Content**:\n    ```bash\n    wp post create --post_type=post --post_title=\"Secret Draft\" --post_content=\"FLAG{IDOR_INFO_DISCLOSURE}\" --post_status=draft\n    # Note the resulting Post ID (e.g., 10)\n    ```\n\n2.  **Create a Vulnerable Form**:\n    Create an `mw-wp-form` post. Use `wp eval` to set the necessary meta data if not using the UI.\n    ```bash\n    # Create the form\n    FORM_ID=$(wp post create --post_type=mw-wp-form --post_title=\"Exploit Form\" --post_status=publish --porcelain)\n    \n    # Enable the 'querystring' setting (URL parameter feature)\n    # The setting is typically stored in the 'mw-wp-form' post meta under a specific key.\n    # In versions \u003C= 5.1.2, settings are serialized in the post meta.\n    wp post bash-eval $FORM_ID 'update_post_meta($post_id, \"querystring\", \"1\");'\n    \n    # Set the form content to include a leak tag\n    wp post update $FORM_ID --post_content='Title: {post_title} | Content: {post_content}'\n    ```\n\n3.  **Embed the Form**:\n    ```bash\n    wp post create --post_type=page --post_title=\"Public Form Page\" --post_status=publish --post_content=\"[mwform_formkey key='mwform_$FORM_ID']\"\n    # Note the resulting Page URL (e.g., \u002Fpublic-form-page\u002F)\n    ```\n\n### 6. Exploitation Strategy\nPerform an unauthenticated `GET` request to the page hosting the form, specifying the `post_id` of the draft post created in step 5.\n\n**Request**:\n*   **Method**: `GET`\n*   **URL**: `http:\u002F\u002Flocalhost:8080\u002Fpublic-form-page\u002F?post_id=[DRAFT_ID]`\n*   **Headers**: None required.\n\n**Payload**:\nNo payload in the body is required; the payload is the `post_id` query parameter targeting a non-public post.\n\n### 7. Expected Results\n*   **Response Status**: 200 OK.\n*   **Response Body**: The HTML of the form will contain the string: `Title: Secret Draft | Content: FLAG{IDOR_INFO_DISCLOSURE}`.\n*   The parser successfully extracted data from a post with `post_status='draft'`, which is otherwise inaccessible to unauthenticated users.\n\n### 8. Verification Steps\n1.  **Verify Unauthenticated Access**: Ensure the request is made without any session cookies or `Authorization` headers.\n2.  **Confirm Disclosure**: Use `grep` or similar logic to find the \"FLAG\" string in the response body.\n3.  **Check Post Status**: Confirm via WP-CLI that the targeted post is indeed a draft:\n    ```bash\n    wp post get [DRAFT_ID] --field=post_status\n    ```\n\n### 9. Alternative Approaches\n*   **Meta Disclosure**: If the form contains tags for custom meta keys (e.g., `{_wp_page_template}` or `{secret_api_key}`), these can also be disclosed by changing the placeholder in the form content.\n*   **Password Protected Posts**: The same method can be used to extract the content of password-protected posts without providing the password, as `get_post()` and raw property access bypasses the password requirement check implemented in `the_content()` filters.","The MW WP Form plugin for WordPress is vulnerable to information exposure via the 'post_id' query parameter in versions up to 5.1.2. Unauthenticated attackers can exploit this to extract content from draft, private, or password-protected posts by pre-filling form data using URL parameters when specific placeholders like {post_content} are present in the form.","\u002F\u002F classes\u002Fmodels\u002Fclass.parser.php:122\n\tprotected function _get_post_property_from_querystring( $matches ) {\n\t\tif ( ! isset( $_GET['post_id'] ) || ! MWF_Functions::is_numeric( $_GET['post_id'] ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$post = get_post( $_GET['post_id'] );\n\t\tif ( empty( $post->ID ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn $this->_get_post_property( $post, $matches[1] );\n\t}\n\n---\n\n\u002F\u002F classes\u002Fmodels\u002Fclass.parser.php:166\n\tprotected function _get_post_property( $post, $meta_key ) {\n\t\tif ( ! is_a( $post, 'WP_Post' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( isset( $post->$meta_key ) ) {\n\t\t\treturn $post->$meta_key;\n\t\t}\n\n\t\t$post_meta = get_post_meta( $post->ID, $meta_key, true );\n\t\tif ( is_array( $post_meta ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn $post_meta;\n\t}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fabstract\u002Fclass.form-field.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fabstract\u002Fclass.form-field.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fabstract\u002Fclass.form-field.php\t2023-09-20 02:47:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fabstract\u002Fclass.form-field.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -129,7 +129,7 @@\n \t\t$args = $this->set_names();\n \n \t\tif ( empty( $args['shortcode_name'] ) || empty( $args['display_name'] ) ) {\n-\t\t\texit( get_class() . '::set_names() returns not right values. Returned values is ' . serialize( $args ) . ' now.' );\n+\t\t\texit( get_class( $this ) . '::set_names() returns not right values. Returned values is ' . serialize( $args ) . ' now.' );\n \t\t}\n \n \t\t$this->shortcode_name = $args['shortcode_name'];\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fabstract\u002Fclass.validation-rule.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fabstract\u002Fclass.validation-rule.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fabstract\u002Fclass.validation-rule.php\t2023-09-20 02:47:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fabstract\u002Fclass.validation-rule.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -88,8 +88,8 @@\n \t *\u002F\n \tpublic function getName() {\n \t\tMWF_Functions::deprecated_message(\n-\t\t\tget_class() . '::getName()',\n-\t\t\tget_class() . '::get_name()'\n+\t\t\tget_class( $this ) . '::getName()',\n+\t\t\tget_class( $this ) . '::get_name()'\n \t\t);\n \t\treturn $this->get_name();\n \t}\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fmodels\u002Fclass.csrf.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fmodels\u002Fclass.csrf.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fmodels\u002Fclass.csrf.php\t2023-11-29 05:59:58.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fmodels\u002Fclass.csrf.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -40,7 +40,18 @@\n \t\tstatic::$token = ! $saved_token ? static::generate_token() : $saved_token;\n \t\tif ( ! $saved_token && ! headers_sent() ) {\n \t\t\t$secure = apply_filters( 'mwform_secure_cookie', is_ssl() );\n-\t\t\tsetcookie( static::KEY, static::$token, 0, COOKIEPATH, COOKIE_DOMAIN, $secure, true );\n+\t\t\tsetcookie(\n+\t\t\t\tstatic::KEY,\n+\t\t\t\tstatic::$token,\n+\t\t\t\tarray(\n+\t\t\t\t\t'expires'  => 0,\n+\t\t\t\t\t'path'     => COOKIEPATH,\n+\t\t\t\t\t'domain'   => COOKIE_DOMAIN,\n+\t\t\t\t\t'secure'   => $secure,\n+\t\t\t\t\t'httponly' => true,\n+\t\t\t\t\t'samesite' => 'Lax',\n+\t\t\t\t)\n+\t\t\t);\n \t\t}\n \t}\n \ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fmodels\u002Fclass.parser.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fmodels\u002Fclass.parser.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fmodels\u002Fclass.parser.php\t2023-09-20 02:47:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fmodels\u002Fclass.parser.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -134,6 +134,10 @@\n \t\t\treturn;\n \t\t}\n \n+\t\tif ( 'publish' !== $post->post_status || post_password_required( $post ) ) {\n+\t\t\treturn;\n+\t\t}\n+\n \t\treturn $this->_get_post_property( $post, $matches[1] );\n \t}\n \ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fmodels\u002Fclass.session.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fmodels\u002Fclass.session.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fmodels\u002Fclass.session.php\t2023-09-20 02:47:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fmodels\u002Fclass.session.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -44,7 +44,18 @@\n \t\t\t$secure     = apply_filters( 'mwform_secure_cookie', is_ssl() );\n \t\t\ttry {\n \t\t\t\tset_error_handler( array( 'MW_WP_Form_Session', 'error_handler' ) );\n-\t\t\t\tsetcookie( $this->name, $session_id, 0, COOKIEPATH, COOKIE_DOMAIN, $secure, true );\n+\t\t\t\tsetcookie(\n+\t\t\t\t\t$this->name,\n+\t\t\t\t\t$session_id,\n+\t\t\t\t\tarray(\n+\t\t\t\t\t\t'expires'  => 0,\n+\t\t\t\t\t\t'path'     => COOKIEPATH,\n+\t\t\t\t\t\t'domain'   => COOKIE_DOMAIN,\n+\t\t\t\t\t\t'secure'   => $secure,\n+\t\t\t\t\t\t'httponly' => true,\n+\t\t\t\t\t\t'samesite' => 'Lax',\n+\t\t\t\t\t)\n+\t\t\t\t);\n \t\t\t} catch ( ErrorException $e ) {\n \t\t\t\t\u002F\u002F No process...\n \t\t\t}\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fvalidation-rules\u002Fclass.maximagesize.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fvalidation-rules\u002Fclass.maximagesize.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fclasses\u002Fvalidation-rules\u002Fclass.maximagesize.php\t2023-09-20 02:47:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fclasses\u002Fvalidation-rules\u002Fclass.maximagesize.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -56,6 +56,7 @@\n \t\t\t$filepath = MW_WP_Form_Directory::generate_user_filepath( $form_id, $name, $value );\n \t\t}\n \n+\t\t$imagesize = false;\n \t\tif ( file_exists( $filepath ) && exif_imagetype( $filepath ) ) {\n \t\t\t$imagesize = getimagesize( $filepath );\n \t\t} else {\n@@ -70,7 +71,7 @@\n \t\t\t'message' => __( 'This image size is too big.', 'mw-wp-form' ),\n \t\t);\n \t\t$options  = array_merge( $defaults, $options );\n-\t\tif ( $is_error || $imagesize[0] > $options['width'] || $imagesize[1] > $options['height'] ) {\n+\t\tif ( $is_error || ( is_array( $imagesize ) && ( $imagesize[0] > $options['width'] || $imagesize[1] > $options['height'] ) ) ) {\n \t\t\treturn $options['message'];\n \t\t}\n \t}\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fmw-wp-form.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fmw-wp-form.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.2\u002Fmw-wp-form.php\t2026-04-08 02:35:24.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fmw-wp-form\u002F5.1.3\u002Fmw-wp-form.php\t2026-04-27 00:06:30.000000000 +0000\n@@ -3,7 +3,7 @@\n  * Plugin Name: MW WP Form\n  * Plugin URI: https:\u002F\u002Fmw-wp-form.web-soudan.co.jp\n  * Description: MW WP Form is shortcode base contact form plugin. This plugin have many features. For example you can use many validation rules, inquiry data saving, and chart aggregation using saved inquiry data.\n- * Version: 5.1.2\n+ * Version: 5.1.3\n  * Requires at least: 6.0\n  * Requires PHP: 8.0\n  * Author: websoudan\n@@ -80,6 +80,7 @@\n \t\t\tadd_action( 'admin_menu', array( $this, '_admin_menu_for_chart' ) );\n \t\t\tadd_action( 'admin_menu', array( $this, '_admin_menu_for_inquiry_data_list' ) );\n \t\t\tadd_action( 'current_screen', array( $this, '_current_screen' ) );\n+\t\t\tnew MW_WP_Form_Deprecation_Notice_Controller();\n \t\t} elseif ( ! is_admin() ) {\n \t\t\tnew MW_WP_Form_Main_Controller();\n \t\t}","The exploit targets forms with the 'querystring' (URL parameter) setting enabled. An unauthenticated attacker identifies a page hosting such a form that uses post placeholders (like {post_content} or {post_title}). By sending a GET request to the form page with the 'post_id' query parameter set to the ID of a draft, private, or password-protected post, the plugin's parser retrieves the unauthorized post object and renders its sensitive properties directly into the form's HTML response.","gemini-3-flash-preview","2026-05-14 16:52:52","2026-05-14 16:54:16",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","5.1.2","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmw-wp-form\u002Ftags\u002F5.1.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fmw-wp-form.5.1.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmw-wp-form\u002Ftags\u002F5.1.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fmw-wp-form.5.1.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fmw-wp-form\u002Ftags"]