CVE-2026-6080

Tutor LMS <= 3.9.8 - Authenticated (Admin+) SQL Injection via 'date' Parameter

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

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: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<=3.9.8
PublishedApril 16, 2026
Last updatedApril 17, 2026
Affected plugintutor

What Changed in the Fix

Changes introduced in v3.9.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

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 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_data or tutor_report_sales_data).
  • Vulnerable Parameter: date (or potentially start_date/end_date depending on the specific report).
  • Authentication: Requires Admin level privileges (capability manage_options or tutor_manage_reports).
  • Payload Type: Time-based blind SQL Injection or Error-based (if WP_DEBUG is enabled).

3. Code Flow (Inferred)

  1. An Administrator accesses the Tutor LMS Reports menu.
  2. The UI (React/Vue based in 3.x) sends an AJAX request to admin-ajax.php.
  3. The PHP handler (e.g., Tutor\Reports\Analytics::get_report) retrieves the date parameter: $date = $_POST['date'];.
  4. The code constructs a SQL fragment: $date_query = " AND post_date = '$date'";.
  5. The fragment is appended to a larger query: $query = "SELECT ... WHERE 1=1 " . $date_query;.
  6. The result is passed to prepare(): $wpdb->get_results( $wpdb->prepare( $query, $other_params ) );.
  7. Since $date was 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.

  1. Identify Shortcode/Page: Navigate to the Tutor LMS Reports page: /wp-admin/admin.php?page=tutor-reports.
  2. Extract Nonce: The plugin localizes its settings into a global JavaScript object. Use the browser_eval tool to find it.
  3. Variable Name: Usually _tutorobject or tutor_admin.
  4. Action: In assets/js/tutor-front.js, the code references _tutorobject.ajaxurl.
  5. Target Nonce Path: window._tutorobject?.nonce or window.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

  1. Install Tutor LMS 3.9.8.
  2. Activate Plugin.
  3. 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.
  4. Create Admin User: Ensure you have the credentials for a user with the administrator role.

7. Expected Results

  • Baseline Request: A normal request with date=2024-01-01 should 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:

  1. Identify the PHP file containing the query (e.g., classes/Reports.php).
  2. Verify if the date variable is being escaped using esc_sql() or if it is indeed interpolated directly before prepare().
  3. 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_date in the same report handlers.
  • period parameter 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 if WP_DEBUG is on or plugin logs are visible).
  • Check the tutor_report_sales_data action specifically, as it is a common target for analytics-based SQLi.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/classes/Reports/Analytics.php
+++ b/classes/Reports/Analytics.php
@@ -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.