Skip to content

Security: Systemic cross-instructor course IDOR + student rating IDOR + privilege escalation (~8 findings) #76

@lighthousekeeper1212

Description

@lighthousekeeper1212

Summary

Multiple authorization vulnerabilities in uLearn allow any instructor to view, edit, and delete other instructors' courses, allow students to manipulate ratings and access resources without enrollment, and allow any user to become an instructor without admin approval.

Vulnerability Details

1. No Resource-Level Authorization (CRITICAL — Systemic)

The application does not use Laravel Policies, Gates, or any resource-level authorization. All instructor endpoints use Course::find($course_id) (or similar model lookups) without verifying that the authenticated instructor owns the course. This affects all course management endpoints:

  • View any course details
  • Edit any course content (title, description, pricing, etc.)
  • Delete any course
  • Manage sections of any course
  • Manage lectures of any course
  • Manage resources/files of any course

2. Course Takeover via instructorCourseInfoSave (CRITICAL)

The instructorCourseInfoSave method (around line 497) sets instructor_id to the current authenticated user when saving. An attacker can call this endpoint with another instructor's course_id, and the course's instructor_id will be overwritten to the attacker's ID — effectively stealing the course.

3. Student Rating Deletion IDOR (HIGH)

The deleteRating method accepts a rating ID and deletes it without verifying the authenticated user owns that rating. Any student can delete any other student's course rating.

4. Student Rating Overwrite IDOR (HIGH)

The courseRate method accepts a rating_id parameter. When provided, it updates that rating record. There is no ownership check, so any student can overwrite any other student's rating content and score.

5. Open Instructor Registration (MEDIUM)

The /become-instructor endpoint allows any authenticated user to become an instructor without admin approval. This expands the attack surface for all instructor-level IDOR vulnerabilities above.

6. SQL Logic Bug in Instructor Course Search (MEDIUM)

The instructor course search query uses orWhere which breaks the instructor_id filter. When a search term is provided, the query returns courses from ALL instructors that match the search term, not just the current instructor's courses. This leaks other instructors' course data.

7. Hardcoded Encryption Key (MEDIUM)

SiteHelpers.php contains a hardcoded encryption key. Since this is a public repository, the key provides no security — any attacker can decrypt data encrypted with it.

8. Resource Download Without Enrollment Check (MEDIUM)

Students can download course resources (files, attachments) without verifying they are enrolled in the course.

Impact

  • Any instructor can view, edit, delete, or steal any other instructor's courses
  • Any student can delete or overwrite any course rating
  • Any user can self-promote to instructor without approval
  • Course search leaks data across instructor boundaries

Recommended Fixes

  1. Add ownership checks to all instructor endpoints:

    // Instead of:
    $course = Course::find($course_id);
    
    // Use:
    $course = Course::where('id', $course_id)
        ->where('instructor_id', auth()->user()->instructor->id)
        ->firstOrFail();
  2. Add ownership checks to rating operations — verify user_id matches authenticated user

  3. Require admin approval for instructor registration

  4. Fix the search query — use a closure for the orWhere to scope it within the instructor filter:

    ->where('instructor_id', $instructorId)
    ->where(function($q) use ($search) {
        $q->where('title', 'like', "%$search%")
          ->orWhere('description', 'like', "%$search%");
    })
  5. Add enrollment checks before allowing resource downloads

  6. Remove or externalize the hardcoded encryption key

Disclosure

This report is intended to help improve the security of uLearn. I'm happy to clarify any of these findings.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions