CVE-2025-13722

Fluent Forms <= 6.1.7 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Form Creation via AI Builder

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
6.1.8
Patched in
1d
Time to patch

Description

The Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 6.1.7. This is due to missing capability checks on the `fluentform_ai_create_form` AJAX action. This makes it possible for authenticated attackers, with Subscriber-level access and above, to create arbitrary forms via the publicly exposed AI builder.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=6.1.7
PublishedJanuary 6, 2026
Last updatedJanuary 7, 2026
Affected pluginfluentform

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps required to demonstrate the missing authorization vulnerability in Fluent Forms (CVE-2025-13722). ### 1. Vulnerability Summary The vulnerability exists in the `fluentform_ai_create_form` AJAX action of the Fluent Forms plugin. While the functionality is intende…

Show full research plan

This research plan outlines the steps required to demonstrate the missing authorization vulnerability in Fluent Forms (CVE-2025-13722).

1. Vulnerability Summary

The vulnerability exists in the fluentform_ai_create_form AJAX action of the Fluent Forms plugin. While the functionality is intended for administrators using the AI form builder, the AJAX handler lacks a capability check (e.g., current_user_can('manage_options')). This oversight allows any authenticated user, including those with the lowest privileges (Subscriber), to invoke the function and create arbitrary forms on the WordPress site.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: fluentform_ai_create_form
  • Method: POST
  • Required Authentication: Subscriber level or higher.
  • Preconditions: The AI Builder module must be active (usually active by default if the plugin is configured).
  • Vulnerable Parameter: The payload likely expects form configuration data, possibly under a form_data or data key, or parameters describing the form "prompt" if the AI processing happens server-side.

3. Code Flow (Inferred from Patch and Description)

  1. Registration: The plugin registers the action:
    add_action('wp_ajax_fluentform_ai_create_form', [$this, 'createFormViaAi']);
  2. Trigger: A Subscriber user sends a POST request to admin-ajax.php with action=fluentform_ai_create_form.
  3. Vulnerable Function: The handler function (e.g., createFormViaAi) is called.
  4. Authorization Gap: The function likely checks for a valid nonce but fails to verify if the user has the fluentform_dashboard_access or manage_options capability.
  5. Sink: The function processes the input and calls FluentForm\App\Modules\Form\Form::create(), inserting a new form into the wp_fluentform_forms table.

4. Nonce Acquisition Strategy

Fluent Forms typically localizes its AJAX nonces for the admin dashboard. Even a Subscriber user can access the WordPress admin dashboard (/wp-admin/index.php), where the plugin may enqueue its global variables.

  1. Identify Variable: Fluent Forms often uses fluent_forms_global_var or fluentform_admin_vars.
  2. Creation/Navigation:
    • Log in as a Subscriber.
    • Navigate to /wp-admin/.
  3. Extraction:
    • Use browser_eval to find the nonce:
      browser_eval("window.fluent_forms_global_var?.nonce") or browser_eval("window.fluentform_admin_vars?.nonce").
    • If the nonce is not present on the dashboard, it may be necessary to navigate to a page where a Fluent Form is rendered.

5. Exploitation Strategy

The goal is to create a form with a specific title to prove unauthorized creation.

Step 1: Setup a Subscriber User
Create a low-privileged user to act as the attacker.

Step 2: Authenticate and Extract Nonce
Use the browser_navigate tool to log in as the Subscriber and find the required nonce.

Step 3: Send the Exploitation Request
Submit the malicious request to create a form.

  • URL: http://[target]/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: fluentform_ai_create_form
    • nonce: [EXTRACTED_NONCE]
    • title: Exploit Form (inferred parameter)
    • form_settings: {"title": "Pwned Form"} (inferred JSON structure)

Note: Since the exact structure of the AI generation payload may vary, if the initial attempt fails, the agent should inspect the plugin's JS assets (e.g., assets/js/fluent_form_admin.js) to find the exact key-value pairs sent to fluentform_ai_create_form.

6. Test Data Setup

  1. Install Plugin: Ensure Fluent Forms version <= 6.1.7 is installed and active.
  2. Create User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123

7. Expected Results

  • Response: The server should return a JSON response, likely containing the ID of the newly created form: {"success": true, "data": {"form_id": X}}.
  • HTTP Status: 200 OK.
  • Unauthorized State: A Subscriber-level user has successfully performed an administrative action.

8. Verification Steps

After sending the HTTP request, verify the form creation using WP-CLI:

# List all forms and look for "Exploit Form"
wp db query "SELECT title FROM wp_fluentform_forms WHERE title = 'Exploit Form';"

Alternatively, if the fluentform CLI is available:

wp fluentform forms list

9. Alternative Approaches

  • JSON Payload: If x-www-form-urlencoded fails, the AI builder might expect a raw JSON POST body. Try sending Content-Type: application/json with the action included in the JSON.
  • Context Discovery: If the nonce is not found in the global object, use browser_eval to search for all localized scripts:
    browser_eval("Object.keys(window).filter(key => key.toLowerCase().includes('fluent'))")
  • AI Prompt Payload: The "AI Builder" might require a "prompt" string rather than a direct form structure. Try:
    action=fluentform_ai_create_form&prompt=Create+a+simple+contact+form&nonce=[NONCE]
Research Findings
Static analysis — not yet PoC-verified

Summary

The Fluent Forms plugin for WordPress is vulnerable to a missing authorization check on the 'fluentform_ai_create_form' AJAX action. This allows authenticated users with minimal privileges, such as Subscribers, to bypass intended administrative restrictions and create arbitrary forms via the AI builder functionality.

Vulnerable Code

// fluentform/app/Modules/AIBuider/Handler.php (approximate)

public function createFormViaAi()
{
    $nonce = $_REQUEST['nonce'];
    if (!wp_verify_nonce($nonce, 'fluentform_admin_ajax_nonce')) {
        wp_send_json_error(['message' => 'Invalid nonce'], 422);
    }

    // Vulnerability: Lack of capability check (e.g., current_user_can('fluentform_dashboard_access'))
    // permits any authenticated user to trigger the form creation logic below.

    $prompt = sanitize_text_field($_POST['prompt']);
    // ... processing logic to generate and save the form ...
}

Security Fix

--- a/fluentform/app/Modules/AIBuider/Handler.php
+++ b/fluentform/app/Modules/AIBuider/Handler.php
@@ -10,6 +10,10 @@
             wp_send_json_error(['message' => 'Invalid nonce'], 422);
         }
 
+        if (!current_user_can('fluentform_dashboard_access')) {
+            wp_send_json_error(['message' => 'Permission denied'], 403);
+        }
+
         $prompt = sanitize_text_field($_POST['prompt']);

Exploit Outline

The exploit involves an authenticated attacker with Subscriber-level access targeting the admin AJAX endpoint. The attacker first extracts a valid AJAX nonce from the 'fluentform_admin_vars' or 'fluent_forms_global_var' JavaScript objects available in the WordPress dashboard. Then, they send a POST request to '/wp-admin/admin-ajax.php' with the 'action' set to 'fluentform_ai_create_form', including the nonce and a 'prompt' or 'title' parameter describing the form they wish to create. Because the server-side handler fails to check for administrative capabilities, it processes the request and creates a new form entry in the database.

Check if your site is affected.

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