CVE-2025-15441

Form Maker by 10Web – Mobile-Friendly Drag & Drop Contact Form Builder < 1.15.38 - Unauthenticated SQL Injection

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
1.15.38
Patched in
43d
Time to patch

Description

The Form Maker by 10Web – Mobile-Friendly Drag & Drop Contact Form Builder plugin for WordPress is vulnerable to SQL Injection in versions up to 1.15.38 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers 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:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<1.15.38
PublishedMarch 23, 2026
Last updatedMay 4, 2026
Affected pluginform-maker

What Changed in the Fix

Changes introduced in v1.15.38

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

```markdown # Exploitation Research Plan - CVE-2025-15441 (Form Maker by 10Web) ## 1. Vulnerability Summary The **Form Maker by 10Web** plugin (versions < 1.15.38) is vulnerable to unauthenticated SQL injection. The vulnerability arises because the plugin uses a common AJAX dispatcher (`form_maker_…

Show full research plan
# Exploitation Research Plan - CVE-2025-15441 (Form Maker by 10Web)

## 1. Vulnerability Summary
The **Form Maker by 10Web** plugin (versions < 1.15.38) is vulnerable to unauthenticated SQL injection. The vulnerability arises because the plugin uses a common AJAX dispatcher (`form_maker_ajax`) for both authenticated and unauthenticated actions. Specifically, the unauthenticated `nopriv_formmakerwdcaptcha` action calls the same callback as administrative actions like `FormMakerSubmits`. If this dispatcher or the subsequent controller logic fails to verify the user's capabilities for a specific task, an unauthenticated attacker can execute administrative database queries. The SQL injection itself typically resides in the handling of parameters like `order_by` or `filter_id` within the submissions model, where user input is concatenated into SQL queries without proper preparation or whitelisting.

## 2. Attack Vector Analysis
- **Endpoint:** `wp-admin/admin-ajax.php`
- **Action:** `formmakerwdcaptcha` (The `nopriv_` version allows unauthenticated access).
- **Task (Vulnerable Dispatcher Logic):** `FormMakerSubmits` (inferred task used for managing form entries).
- **Vulnerable Parameter:** `order_by` (Commonly vulnerable in this plugin family).
- **Payload Type:** UNION-based or Time-based SQL Injection.
- **Authentication:** None required (Unauthenticated).

##
Research Findings
Static analysis — not yet PoC-verified

Summary

The Form Maker plugin for WordPress is vulnerable to unauthenticated SQL injection via the SQL Mapping feature. The vulnerability exists because user-supplied form values and metadata (like IP addresses) are directly concatenated into SQL queries using string replacement instead of proper preparation or escaping.

Vulnerable Code

// frontend/models/form_maker.php line 2589
    $user_fields = array(
      "ip" => $ip,
      "subid" => $group_id,
      "userid" => $wp_userid,
      "username" => $wp_username,
      "useremail" => $wp_useremail,
    );

---

// frontend/models/form_maker.php line 2622
        foreach ( array_keys($fvals) as $fval_key ) {
	      $query = str_replace('"' . $fval_key . '"', $fval_key, $query);
	      $query = str_replace('\'' . $fval_key . '\'', $fval_key, $query);
	      $query = str_replace('`' . $fval_key . '`', $fval_key, $query);
        }
        $query_values = array();
        $query = preg_replace_callback('/\{([^}]+)\}/', function($match) use ($fvals, &$query_values) {
	      $placeholder_key = $match[0];
	      if ( isset($fvals[$placeholder_key]) ) {
		      $query_values[] = $fvals[$placeholder_key];
		      return '"%s"';
	      }
	      return '"' . $match[0] . '"';
        }, $query);
        foreach ( $user_fields as $user_key => $user_field ) {
          $query = str_replace('{' . $user_key . '}', $user_field, $query);
        }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/form-maker/1.15.37/frontend/models/form_maker.php /home/deploy/wp-safety.org/data/plugin-versions/form-maker/1.15.38/frontend/models/form_maker.php
--- /home/deploy/wp-safety.org/data/plugin-versions/form-maker/1.15.37/frontend/models/form_maker.php	2026-01-26 10:29:22.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/form-maker/1.15.38/frontend/models/form_maker.php	2026-03-18 10:07:04.000000000 +0000
@@ -2589,11 +2589,11 @@
       $html_list .= '</table>';
     }
     $user_fields = array(
-      "ip" => $ip,
-      "subid" => $group_id,
-      "userid" => $wp_userid,
-      "username" => $wp_username,
-      "useremail" => $wp_useremail,
+      "{ip}" => $ip,
+      "{subid}" => $group_id,
+      "{userid}" => $wp_userid,
+      "{username}" => $wp_username,
+      "{useremail}" => $wp_useremail,
     );
     $queries = $wpdb->get_results($wpdb->prepare("SELECT * FROM " . $wpdb->prefix . "formmaker_query WHERE form_id=%d", (int) $id));
     if ( $queries ) {
@@ -2618,23 +2618,16 @@
         $temp = explode('***wdfdatabasewdf***', $temp[1]);
         $database = $temp[0];
         $query = $query->query;
-        foreach ( array_keys($fvals) as $fval_key ) {
-	      $query = str_replace('"' . $fval_key . '"', $fval_key, $query);
-	      $query = str_replace('\'' . $fval_key . '\'', $fval_key, $query);
-	      $query = str_replace('`' . $fval_key . '`', $fval_key, $query);
-        }
         $query_values = array();
-        $query = preg_replace_callback('/\{([^}]+)\}/', function($match) use ($fvals, &$query_values) {
+		$query_keys = array_merge($fvals, $user_fields);
+        $query = preg_replace_callback('/\{([^}]+)\}/', function($match) use ($query_keys, &$query_values) {
 	      $placeholder_key = $match[0];
- 	      if ( isset($fvals[$placeholder_key]) ) {
-		      $query_values[] = $fvals[$placeholder_key];
+	      if ( isset($query_keys[$placeholder_key]) ) {
+		      $query_values[] = $query_keys[$placeholder_key];
 		      return '"%s"';
 	      }
 	      return '"' . $match[0] . '"';
         }, $query);
-        foreach ( $user_fields as $user_key => $user_field ) {
-          $query = str_replace('{' . $user_key . '}', $user_field, $query);
-        }
         if ( $con_type == 'remote' ) {
           $wpdb_temp = new wpdb($username, $password, $database, $host);
           if ( !empty($query_values) ) {

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker can submit a form that has the 'SQL Mapping' feature enabled. By injecting SQL syntax into form fields that correspond to query placeholders (e.g., fields substituted into the query via the {placeholder} syntax), the attacker can bypass string replacement logic because the plugin fails to call $wpdb->prepare() or similar escaping mechanisms on these inputs before executing the final query. The attack can also be mounted by spoofing request headers like 'X-Forwarded-For' if the {ip} placeholder is used in a mapped query, as the user_fields array was also replaced via insecure str_replace.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.