
Authenticated IDOR / Broken Access Control in Tutor LMS Plugin
Disclaimer: This repository is created for educational purposes and ethical disclosure only. The vulnerability has been responsibly reported to the vendor and patched. Do not use this information to exploit systems without proper authorization.
Insecure Direct Object Reference (IDOR) / Broken Access Control vulnerability was discovered in the Tutor LMS plugin for WordPress (versions <= 3.9.5). This flaw allows a low-privileged user with the Tutor Instructor role to perform unauthorized bulk actions, such as changing the publication status or permanently deleting courses owned by other instructors or administrators.
By intercepting a legitimate backend request and tampering with the course ID parameter, an attacker can bypass intended access controls. In a real-world scenario, this allows malicious actors to sabotage competitor courses on a multi-instructor marketplace, causing direct business disruption, revenue loss, and reputational damage.
<= 3.9.53.9.6The core issue stems from missing object-level authorization checks within the plugin's bulk action handler.
File: tutor/classes/Course_List.php
Function: course_list_bulk_action()
1. Unvalidated Object Identifiers:
The function accepts user-controlled inputs (bulk-action and bulk-ids) directly from the HTTP request. While the plugin verifies that the user holds a general capability to manage courses, it entirely fails to verify per-course ownership for the specific IDs supplied in the bulk-ids array.
2. Execution Without Ownership Verification:
When the status update path (update_course_status()) or the deletion path (bulk_delete_course()) is triggered, the code iterates over the attacker-supplied IDs. It executes database operations (e.g., updating cp_posts.post_status) using a WHERE ID IN (...) clause without appending a condition to ensure $post_author == $current_user_id.
Because the handler does not enforce object-level authorization, any instructor with access to the bulk action workflow can manipulate arbitrary course IDs.
The following steps demonstrate how an authenticated Tutor Instructor can change the status of another instructor's course.
course_id belonging to another instructor (Victim). (e.g., ID: 27)./wp-admin/admin.php?page=tutor). Select any course you own, choose a status change action (e.g., set to "draft"), and click "Apply".POST request to /wp-admin/admin-ajax.php.status parameter to trash (or pending, private).id parameter from your course ID to the Victim's course ID (27).{"success":true}. The victim's course is successfully moved to the trash and disappears from the public frontend.