CVE-2026-31922

Fox LMS <= 1.0.6.3 - Authenticated (Contributor+) SQL Injection

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

Description

The Fox LMS plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 1.0.6.3 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 contributor-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: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<=1.0.6.3
PublishedFebruary 7, 2026
Last updatedApril 15, 2026
Affected pluginfox-lms

What Changed in the Fix

Changes introduced in v1.0.6.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-31922 (Fox LMS SQL Injection) ## 1. Vulnerability Summary The Fox LMS plugin (version <= 1.0.6.3) contains an authenticated SQL injection vulnerability. The issue exists in how the plugin handles database queries for its administrative list tables, specificall…

Show full research plan

Exploitation Research Plan: CVE-2026-31922 (Fox LMS SQL Injection)

1. Vulnerability Summary

The Fox LMS plugin (version <= 1.0.6.3) contains an authenticated SQL injection vulnerability. The issue exists in how the plugin handles database queries for its administrative list tables, specifically within the orderby or order parameters. These parameters are often directly concatenated into SQL strings without proper preparation using $wpdb->prepare().

This allows a user with Contributor level permissions or higher (who may be designated as an Instructor in the LMS) to append arbitrary SQL commands to existing queries, enabling the extraction of sensitive database information (like hashes from the wp_users table).

2. Attack Vector Analysis

  • Vulnerable Endpoints:
    • /wp-admin/admin.php?page=fox-lms-lesson-reports
    • /wp-admin/admin.php?page=fox-lms-qna
  • Vulnerable Parameters: orderby (most likely) and order.
  • Required Authentication: Authenticated user with Contributor (Instructor) level access.
  • Preconditions:
    • The plugin must be active.
    • To access Lesson Reports, the "Enable Lesson Reports" option may need to be enabled in settings (though the code in fox-lms-lesson-reports-display.php doesn't explicitly block access if it's off, only the settings partial mentions it).
  • Nonce Requirement: The partials for Lesson Reports and Q&A (found in admin/partials/) explicitly check for a nonce: wp_verify_nonce(..., 'foxlms_admin_nonce').

3. Code Flow

  1. Entry Point: A Contributor-level user requests the fox-lms-lesson-reports page.
  2. Controller Logic: The main admin class (inferred as Fox_Lms_Admin) handles the menu page request and includes admin/partials/lesson-reports/fox-lms-lesson-reports-display.php.
  3. Nonce Check: The file verifies foxlms_admin_nonce stored in $this->fox_lms_nonce.
  4. List Table Initialization: The controller instantiates the list table (e.g., Fox_Lms_Lesson_Reports_List_Table) and calls $this->lesson_reports_obj->prepare_items().
  5. Vulnerable Sink: Inside prepare_items(), the code retrieves $_GET['orderby'] and $_GET['order']. It likely constructs the query like this:
    $orderby = !empty($_GET['orderby']) ? $_GET['orderby'] : 'id';
    $order = !empty($_GET['order']) ? $_GET['order'] : 'asc';
    $query = "SELECT * FROM {$wpdb->prefix}fox_lms_reports ORDER BY $orderby $order";
    $wpdb->get_results($query);
    
  6. Injection: By providing a payload in orderby, the attacker controls the ORDER BY clause of the query.

4. Nonce Acquisition Strategy

The plugin uses a nonce named foxlms_admin_nonce. This nonce is required even to view the display partials. It is typically localized for the admin environment in a variable called FoxLmsAdmin.

Strategy:

  1. Log in to WordPress as a Contributor.
  2. Navigate to a Fox LMS admin page that does not require the nonce for its own display (e.g., the main plugin dashboard).
  3. Extract the nonce from the FoxLmsAdmin JS object.

Steps:

  1. Navigate: browser_navigate("/wp-admin/admin.php?page=fox-lms-dashboard") (or any other visible plugin page).
  2. Extract: browser_eval("window.FoxLmsAdmin?.nonce")
  3. Result: Use this value as the _wpnonce parameter in subsequent requests.

5. Exploitation Strategy

We will perform a time-based SQL injection using the orderby parameter.

Step-by-Step Plan:

  1. Target URL: /wp-admin/admin.php?page=fox-lms-lesson-reports
  2. Method: GET
  3. Payload: (CASE WHEN (1=1) THEN ID ELSE (SELECT 1 FROM (SELECT(SLEEP(5)))x) END)
  4. Request Construction:
    • page: fox-lms-lesson-reports
    • _wpnonce: [EXTRACTED_NONCE]
    • orderby: (SELECT 1 FROM (SELECT(SLEEP(5)))x)
    • order: asc

Example Exploit Request:

GET /wp-admin/admin.php?page=fox-lms-lesson-reports&_wpnonce=a1b2c3d4e5&orderby=(SELECT+1+FROM+(SELECT(SLEEP(5)))x)&order=asc HTTP/1.1
Host: localhost
Cookie: [CONTRIBUTOR_COOKIES]

6. Test Data Setup

  1. User: Create a user with the contributor role.
  2. Settings: Ensure the plugin is active.
  3. Reports: If the fox-lms-lesson-reports page is empty, the injection might still work if the query is executed before checking for results. It is best to have at least one record in the relevant database table (e.g., a dummy lesson report).
  4. Shortcode/Page: No shortcode is needed as this is an admin-side (authenticated) vulnerability.

7. Expected Results

  • Baseline: A request with orderby=id should return immediately (~0.1s).
  • Exploit: A request with orderby=(SELECT+1+FROM+(SELECT(SLEEP(5)))x) should delay the response by exactly 5 seconds.
  • Data Extraction: Using IF and ASCII(SUBSTRING(...)) logic in the orderby clause, we can exfiltrate the admin user's hash character by character via time delays.

8. Verification Steps

  1. Confirm Payload: Verify the delay in the http_request response time.
  2. Cross-Check: Change 1=1 to 1=2 in a conditional payload to ensure the delay disappears.
  3. DB Check: Use wp db query "SELECT ..." via WP-CLI to confirm the presence of data in the table being queried.

9. Alternative Approaches

If fox-lms-lesson-reports is inaccessible:

  1. Alternative Page: Try page=fox-lms-qna.
  2. Error-Based: If WP_DEBUG is on, attempt error-based injection using extractvalue() or updatexml() in the orderby parameter.
  3. AJAX Route: Use the fox_lms_admin_ajax action seen in admin/js/admin.js if it supports a function that queries the database with user-controlled parameters.
    • Action: fox_lms_admin_ajax
    • Function: (Enumerate possible function values via grep on the full source if available).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Fox LMS plugin for WordPress is vulnerable to authenticated SQL injection via the 'orderby' and 'order' parameters in its administrative list tables. This occurs because user-supplied sorting parameters are concatenated directly into SQL queries without proper sanitization or validation against a whitelist, allowing Contributor-level users (Instructors) to exfiltrate sensitive data via time-based techniques.

Vulnerable Code

// admin/partials/lesson-reports/fox-lms-lesson-reports-display.php line 49
$this->lesson_reports_obj->process_bulk_action();
$this->lesson_reports_obj->prepare_items();
$this->lesson_reports_obj->display();

---

// Conceptual vulnerable sink within the prepare_items() method of Fox_Lms_Lesson_Reports_List_Table
// or similar list table classes used in the admin partials:
$orderby = !empty($_GET['orderby']) ? $_GET['orderby'] : 'id';
$order = !empty($_GET['order']) ? $_GET['order'] : 'asc';
$query = "SELECT * FROM {$wpdb->prefix}fox_lms_reports ORDER BY $orderby $order";
$wpdb->get_results($query);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/fox-lms/1.0.6.3/admin/js/admin.js /home/deploy/wp-safety.org/data/plugin-versions/fox-lms/1.0.6.4/admin/js/admin.js
--- /home/deploy/wp-safety.org/data/plugin-versions/fox-lms/1.0.6.3/admin/js/admin.js	2026-02-11 04:59:32.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/fox-lms/1.0.6.4/admin/js/admin.js	2026-02-17 10:30:28.000000000 +0000
@@ -25,25 +25,25 @@
 		}
 
         var toggle_ddmenu = $(document).find('.toggle_ddmenu');
-            toggle_ddmenu.on('click', function () {
-                var ddmenu = $(this).next();
-                var state = ddmenu.attr('data-expanded');
-                switch (state) {
-                    case 'true':
-                        $(this).find('.ays_fa').css({
-                            transform: 'rotate(0deg)'
-                        });
-                        ddmenu.attr('data-expanded', 'false');
-                        break;
-                    case 'false':
-                        $(this).find('.ays_fa').css({
-                            transform: 'rotate(90deg)'
-                        });
-                        ddmenu.attr('data-expanded', 'true');
-                        break;
-                }
-            });
+        toggle_ddmenu.on('click', function () {
+            var ddmenu = $(this).next();
+            var state = ddmenu.attr('data-expanded');
+            switch (state) {
+                case 'true':
+                    $(this).find('.ays_fa').css({
+                        transform: 'rotate(0deg)'
+                    });
+                    ddmenu.attr('data-expanded', 'false');
+                    break;
+                case 'false':
+                    $(this).find('.ays_fa').css({
+                        transform: 'rotate(90deg)'
+                    });
+                    ddmenu.attr('data-expanded', 'true');
+                    break;
+            }
         });
+    });

Exploit Outline

1. Authenticate as a user with at least Contributor-level permissions (the 'Instructor' role in Fox LMS typically grants this). 2. Access any plugin admin dashboard page (e.g., `admin.php?page=fox-lms-dashboard`) and extract the `foxlms_admin_nonce` from the `FoxLmsAdmin` JavaScript object. 3. Identify a vulnerable list table endpoint, such as the Lesson Reports page: `/wp-admin/admin.php?page=fox-lms-lesson-reports`. 4. Craft a GET request to this endpoint, including the valid `_wpnonce` and a time-based SQL injection payload in the `orderby` parameter. 5. Example Payload: `orderby=(SELECT 1 FROM (SELECT(SLEEP(5)))x)` 6. Observe the response delay; a 5-second delay confirms that the arbitrary SQL command was executed by the database engine.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.