The Exploit
Unauthenticated attackers can submit a malicious field value through the public submit_nex_form handler and later trigger it when the record display page renders saved submissions.
curl -i -X POST 'http://TARGET/?nex_form_action=submit_nex_form' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'nex_form_id=1' \
--data-urlencode 'nex_field_1=<script>alert(1)</script>'
curl -i 'http://TARGET/wp-admin/admin.php?page=nex-forms-records&form_id=1' \
| grep -o '<script>alert(1)</script>'
The first request stores attacker-controlled HTML in the form record. The second request demonstrates that the saved payload is emitted raw into the entry page, which in a browser would execute immediately whenever a user views that page.
What the Patch Did
Before:
$set_data .= '<span class="entry_data_name">'.$nf_functions->unformat_records_name($field_name).'</span> : <span class="entry_data_value">'.$field_value.'</span> | ';
$set_data .= '<span class="entry_data_name">'.$nf_functions->unformat_records_name($field_name).'</span> : <span class="entry_data_value"><img src="'.$field_value.'" width="50"/></span> | ';
After:
$set_data .= '<span class="entry_data_name">'.$nf_functions->unformat_records_name($field_name).'</span> : <span class="entry_data_value">'.esc_html($field_value).'</span> | ';
$set_data .= '<span class="entry_data_name">'.$nf_functions->unformat_records_name($field_name).'</span> : <span class="entry_data_value"><img src="'.esc_html($field_value).'" width="50"/></span> | ';
The patch adds esc_html() output escaping for stored form field values before they are concatenated into HTML. This converts characters like <, >, and " into safe entities so attacker-supplied text cannot be interpreted as markup or script.
Root Cause
This is a CWE-79 Stored Cross-Site Scripting issue caused by untrusted submission data flowing from the front-end submit_nex_form() POST handler into the record rendering code without escaping. The plugin accepted attacker-controlled form input as $field_value, stored it, and later built HTML with string concatenation in includes/classes/class.db.php. Because the saved value crossed the browser boundary directly into the page source, an injected <script> or onerror payload executed whenever a user opened the injected entry page.
Why It Works
The single load-bearing fix is the esc_html($field_value) call. If that line were removed, the stored value would still be written raw into the HTML output and the XSS would return. The patch fixes both plain-text output and the image src context, so the same sanitization is applied consistently where $field_value is rendered. The unchanged unformat_records_name($field_name) call is not the critical fix here; the actual exploit is in the value rendering path.
Hardening Checklist
- Use
esc_html()for any user-controlled string that is inserted into HTML body text. - Use
esc_attr()oresc_url()for user-controlled content inside attributes such assrc,href, orvalue. - Sanitize incoming field names and keys with
sanitize_key()and values withsanitize_text_field()/sanitize_textarea_field()before storing. - Protect submission endpoints with
wp_verify_nonce()and authorize access withcurrent_user_can()where appropriate. - Avoid building HTML with raw string concatenation; use WordPress escaping helpers consistently at the output sink.
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-5063