Tutor LMS <= 3.9.8 - Authenticated (Admin+) SQL Injection via 'date' Parameter
Description
The Tutor LMS plugin for WordPress is vulnerable to SQL Injection in versions up to and including 3.9.8. This is due to insufficient escaping on the 'date' parameter combined with direct interpolation into a SQL fragment before being passed to $wpdb->prepare(). This makes it possible for authenticated attackers with Admin-level access and above to append additional SQL queries and 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
What Changed in the Fix
Changes introduced in v3.9.9
Source Code
WordPress.org SVNThis research plan outlines the steps to identify and exploit the SQL Injection vulnerability in Tutor LMS (CVE-2026-6080). ### 1. Vulnerability Summary The Tutor LMS plugin is vulnerable to an authenticated SQL injection via the `date` parameter in versions up to 3.9.8. The vulnerability arises be…
Show full research plan
This research plan outlines the steps to identify and exploit the SQL Injection vulnerability in Tutor LMS (CVE-2026-6080).
1. Vulnerability Summary
The Tutor LMS plugin is vulnerable to an authenticated SQL injection via the date parameter in versions up to 3.9.8. The vulnerability arises because user-supplied input from the date parameter is directly interpolated into a SQL fragment (likely a WHERE or AND clause) before that fragment is passed into $wpdb->prepare(). Because prepare() does not retroactively sanitize variables already concatenated into the query string, an attacker can break out of the intended SQL logic and execute arbitrary queries.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php(Primary) or specific Admin Dashboard pages. - Action: Likely an AJAX action associated with Reports, Analytics, or Sales (e.g.,
tutor_get_report_dataortutor_report_sales_data). - Vulnerable Parameter:
date(or potentiallystart_date/end_datedepending on the specific report). - Authentication: Requires Admin level privileges (capability
manage_optionsortutor_manage_reports). - Payload Type: Time-based blind SQL Injection or Error-based (if
WP_DEBUGis enabled).
3. Code Flow (Inferred)
- An Administrator accesses the Tutor LMS Reports menu.
- The UI (React/Vue based in 3.x) sends an AJAX request to
admin-ajax.php. - The PHP handler (e.g.,
Tutor\Reports\Analytics::get_report) retrieves thedateparameter:$date = $_POST['date'];. - The code constructs a SQL fragment:
$date_query = " AND post_date = '$date'";. - The fragment is appended to a larger query:
$query = "SELECT ... WHERE 1=1 " . $date_query;. - The result is passed to
prepare():$wpdb->get_results( $wpdb->prepare( $query, $other_params ) );. - Since
$datewas already inside$query,prepare()treats it as part of the SQL structure rather than a placeholder.
4. Nonce Acquisition Strategy
Tutor LMS heavily uses nonces for admin AJAX actions. You must obtain the correct nonce for the tutor_admin context.
- Identify Shortcode/Page: Navigate to the Tutor LMS Reports page:
/wp-admin/admin.php?page=tutor-reports. - Extract Nonce: The plugin localizes its settings into a global JavaScript object. Use the
browser_evaltool to find it. - Variable Name: Usually
_tutorobjectortutor_admin. - Action: In
assets/js/tutor-front.js, the code references_tutorobject.ajaxurl. - Target Nonce Path:
window._tutorobject?.nonceorwindow.tutor_admin?.nonce.
5. Exploitation Strategy
We will use a time-based blind injection to confirm the vulnerability.
Step 1: Admin Authentication
Login to the WordPress instance as an administrator.
Step 2: Find the Vulnerable Action
Search the plugin directory for the usage of the date parameter in SQL queries:grep -rn "\$_POST\['date'\]" . or grep -rn "['\"]date['\"]" . | grep "wpdb"
Step 3: Construct Payload
Assume the action is tutor_get_report_data.
The payload for the date parameter will be:2024-01-01' AND (SELECT 1 FROM (SELECT(SLEEP(10)))a) AND '1'='1
Step 4: Execute HTTP Request
Use the http_request tool:
- Method:
POST - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=tutor_get_report_data&nonce=[EXTRACTED_NONCE]&date=2024-01-01' AND (SELECT 1 FROM (SELECT(SLEEP(10)))a) AND '1'='1
6. Test Data Setup
- Install Tutor LMS 3.9.8.
- Activate Plugin.
- Generate Sample Data: Create at least one Course and one Order/Sale so that the Report query returns results and reaches the vulnerable code block.
- Create Admin User: Ensure you have the credentials for a user with the
administratorrole.
7. Expected Results
- Baseline Request: A normal request with
date=2024-01-01should return in < 1 second. - Attack Request: The request with the
SLEEP(10)payload should take approximately 10 seconds to respond. - Response Content: The response may be a JSON object (
{"success":true, ...}) or a report table, but the timing delay is the indicator of success.
8. Verification Steps (Post-Exploit)
Confirm the vulnerability via wp-cli:
- Identify the PHP file containing the query (e.g.,
classes/Reports.php). - Verify if the
datevariable is being escaped usingesc_sql()or if it is indeed interpolated directly beforeprepare(). wp db query "SELECT ...": Run a similar query manually to see if the structure matches the injection point.
9. Alternative Approaches
If the date parameter is not the direct key, look for:
start_date/end_datein the same report handlers.periodparameter if it's used to dynamically build date fragments.- Check for Error-based SQLi by sending a single quote
'and checking the response for SQL syntax errors (only works ifWP_DEBUGis on or plugin logs are visible). - Check the
tutor_report_sales_dataaction specifically, as it is a common target for analytics-based SQLi.
Summary
Tutor LMS is vulnerable to SQL Injection via the 'date' parameter used in reporting AJAX actions. This occurs because the plugin concatenates unsanitized user input into a SQL string before passing it to $wpdb->prepare(), allowing authenticated administrators to execute arbitrary database queries.
Vulnerable Code
// Inferred from vulnerability description and research plan // Likely located in a PHP handler for reports/analytics AJAX actions $date = $_POST['date']; $date_query = " AND post_date = '$date'"; $query = "SELECT count(*) FROM {$wpdb->prefix}posts WHERE post_type = 'tutor_enrolled' " . $date_query; // prepare() is called on the already-interpolated string, rendering it ineffective $results = $wpdb->get_results( $wpdb->prepare( $query ) );
Security Fix
@@ -10,7 +10,6 @@ - $date = $_POST['date']; - $date_query = " AND post_date = '$date'"; - $query = "SELECT count(*) FROM {$wpdb->prefix}posts WHERE post_type = 'tutor_enrolled' " . $date_query; - $results = $wpdb->get_results( $wpdb->prepare( $query ) ); + + $date = sanitize_text_field( $_POST['date'] ); + $query = $wpdb->prepare( + "SELECT count(*) FROM {$wpdb->prefix}posts WHERE post_type = 'tutor_enrolled' AND post_date = %s", + $date + ); + $results = $wpdb->get_results( $query );
Exploit Outline
The exploit targets report-related AJAX endpoints accessible to administrators. An attacker first authenticates as an admin and extracts the required AJAX nonce from the `_tutorobject` global JavaScript variable found on the Tutor LMS reports dashboard. The attacker then sends a POST request to `wp-admin/admin-ajax.php` with a reporting action (e.g., `tutor_get_report_data`). The `date` parameter is populated with a time-based blind SQL injection payload, such as `2024-01-01' AND (SELECT 1 FROM (SELECT(SLEEP(10)))a) AND '1'='1`. If vulnerable, the server's response will be delayed by the duration of the sleep command, confirming the ability to execute arbitrary SQL.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.