VYPR
Medium severity4.3NVD Advisory· Published Sep 18, 2017· Updated May 13, 2026

CVE-2017-12157

CVE-2017-12157

Description

Moodle 3.x course reports allow teachers to view user details from groups they should not be able to access.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Moodle 3.x course reports allow teachers to view user details from groups they should not be able to access.

Vulnerability

In Moodle 3.x, the capability checks in various course reports (e.g., completion report) did not verify that a teacher had group-level visibility before showing details about users. The function report_completion_can_access_user_report() in report/completion/lib.php lacked a call to groups_user_groups_visible(), allowing teachers with the report/completion:view capability to access information about users in groups for which separated groups mode was enabled and the teacher lacked moodle/site:accessallgroups [1][3]. This affects Moodle versions 2.7.15 through 3.3.1 as listed in the SecurityFocus advisory [3].

Exploitation

An attacker with a teacher role on a Moodle course where separated groups are enabled can access the completion report or other course reports that expose user details. The attacker does not need to be assigned to the target group. By simply navigating to the report (e.g., /report/completion/index.php), the teacher can view user list, activity completion status, and other group-restricted information without triggering an access control check [1][3].

Impact

Successful exploitation results in unauthorized information disclosure (confidentiality impact). The teacher can view details about users in groups they are not supposed to see, such as names, activity progress, and other user-specific report data. The attacker gains no write access or execution privileges; the disclosure is limited to the data available in the course reports [1][2].

Mitigation

The vulnerability was fixed in Moodle 3.3.1, 3.2.4, 3.1.7, and 2.7.18 released on September 18, 2017 [1][3]. The fix adds a call to groups_user_groups_visible() in report_completion_can_access_user_report() before granting access based on the report/completion:view capability [1]. Sites cannot upgrade should restrict teacher roles in courses with separated groups or disable the completion report for untrusted teachers. This CVE is not listed on the CISA Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
moodle/moodlePackagist
>= 3.3.0, < 3.3.23.3.2
moodle/moodlePackagist
>= 3.2.0, < 3.2.53.2.5
moodle/moodlePackagist
< 3.1.83.1.8

Affected products

45
  • Moodle/Moodle44 versions
    cpe:2.3:a:moodle:moodle:3.0.0:*:*:*:*:*:*:*+ 43 more
    • cpe:2.3:a:moodle:moodle:3.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.0:beta:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.0:rc1:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.0:rc2:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.0:rc3:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.0:rc4:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.10:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.2:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.3:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.5:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.6:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.7:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.8:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.0.9:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.0:beta:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.0:rc1:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.0:rc2:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.3:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.4:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.5:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.6:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.1.7:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:beta:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:rc1:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:rc2:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:rc3:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:rc4:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.0:rc5:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.2:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.3:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.2.4:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.3.0:*:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.3.0:beta:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.3.0:rc1:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.3.0:rc2:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.3.0:rc3:*:*:*:*:*:*
    • cpe:2.3:a:moodle:moodle:3.3.1:*:*:*:*:*:*:*
  • ghsa-coords
    Range: >= 3.3.0, < 3.3.2

Patches

1
85b531e8beae

MDL-58762 report: Check group permissions in course user reports

https://github.com/moodle/moodleJuan LeyvaMay 2, 2017via ghsa
10 files changed · +67 47
  • report/completion/lib.php+15 10 modified
    @@ -80,25 +80,30 @@ function report_completion_can_access_user_report($user, $course) {
         }
     
         if ($course->id != SITEID and !$course->enablecompletion) {
    -        return;
    +        return false;
         }
     
         $coursecontext = context_course::instance($course->id);
         $personalcontext = context_user::instance($user->id);
     
    -    if (has_capability('report/completion:view', $coursecontext)) {
    -        return true;
    -    }
    -
    -    if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    -        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
    +    if ($user->id == $USER->id) {
    +        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
                 return true;
             }
    -
    -    } else if ($user->id == $USER->id) {
    -        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
    +    } else if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    +        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
                 return true;
             }
    +
    +    }
    +
    +    // Check if $USER shares group with $user (in case separated groups are enabled and 'moodle/site:accessallgroups' is disabled).
    +    if (!groups_user_groups_visible($course, $user->id)) {
    +        return false;
    +    }
    +
    +    if (has_capability('report/completion:view', $coursecontext)) {
    +        return true;
         }
     
         return false;
    
  • report/completion/user.php+1 1 modified
    @@ -45,7 +45,7 @@
         require_login($course);
     }
     
    -if (!report_completion_can_access_user_report($user, $course, true)) {
    +if (!report_completion_can_access_user_report($user, $course)) {
         // this should never happen
         print_error('nocapability', 'report_completion');
     }
    
  • report/log/lang/en/report_log.php+1 0 modified
    @@ -37,6 +37,7 @@
     $string['log:viewtoday'] = 'View today\'s logs';
     $string['page'] = 'Page {$a}';
     $string['logsformat'] = 'Logs format';
    +$string['nocapability'] = 'Can not access user log report';
     $string['nologreaderenabled'] = 'No log reader enabled';
     $string['origin'] = 'Source';
     $string['other'] = 'Other';
    
  • report/log/lib.php+15 15 modified
    @@ -89,6 +89,21 @@ function report_log_can_access_user_report($user, $course) {
         $coursecontext = context_course::instance($course->id);
         $personalcontext = context_user::instance($user->id);
     
    +    if ($user->id == $USER->id) {
    +        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
    +            return array(true, true);
    +        }
    +    } else if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    +        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
    +            return array(true, true);
    +        }
    +    }
    +
    +    // Check if $USER shares group with $user (in case separated groups are enabled and 'moodle/site:accessallgroups' is disabled).
    +    if (!groups_user_groups_visible($course, $user->id)) {
    +        return array(false, false);
    +    }
    +
         $today = false;
         $all = false;
     
    @@ -99,21 +114,6 @@ function report_log_can_access_user_report($user, $course) {
             $all = true;
         }
     
    -    if ($today and $all) {
    -        return array(true, true);
    -    }
    -
    -    if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    -        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
    -            return array(true, true);
    -        }
    -
    -    } else if ($user->id == $USER->id) {
    -        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
    -            return array(true, true);
    -        }
    -    }
    -
         return array($all, $today);
     }
     
    
  • report/log/user.php+4 0 modified
    @@ -58,6 +58,10 @@
     
     list($all, $today) = report_log_can_access_user_report($user, $course);
     
    +if (!$today && !$all) {
    +    print_error('nocapability', 'report_log');
    +}
    +
     if ($mode === 'today') {
         if (!$today) {
             require_capability('report/log:viewtoday', $coursecontext);
    
  • report/outline/lang/en/report_outline.php+1 0 modified
    @@ -26,6 +26,7 @@
     $string['eventactivityreportviewed'] = 'Activity report viewed';
     $string['eventoutlinereportviewed'] = 'Outline report viewed';
     $string['neverseen'] = 'Never seen';
    +$string['nocapability'] = 'Can not access user outline report';
     $string['nologreaderenabled'] = 'No log reader enabled';
     $string['numviews'] = '{$a->numviews} by {$a->distinctusers} users';
     $string['outline:view'] = 'View activity report';
    
  • report/outline/lib.php+14 9 modified
    @@ -70,19 +70,24 @@ function report_outline_can_access_user_report($user, $course) {
         $coursecontext = context_course::instance($course->id);
         $personalcontext = context_user::instance($user->id);
     
    -    if (has_capability('report/outline:view', $coursecontext)) {
    -        return true;
    -    }
    -
    -    if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    -        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
    +    if ($user->id == $USER->id) {
    +        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
                 return true;
             }
    -
    -    } else if ($user->id == $USER->id) {
    -        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
    +    } else if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    +        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
                 return true;
             }
    +
    +    }
    +
    +    // Check if $USER shares group with $user (in case separated groups are enabled and 'moodle/site:accessallgroups' is disabled).
    +    if (!groups_user_groups_visible($course, $user->id)) {
    +        return false;
    +    }
    +
    +    if (has_capability('report/outline:view', $coursecontext)) {
    +        return true;
         }
     
         return false;
    
  • report/outline/user.php+2 2 modified
    @@ -55,8 +55,8 @@
     }
     $PAGE->set_url('/report/outline/user.php', array('id'=>$userid, 'course'=>$courseid, 'mode'=>$mode));
     
    -if (!report_outline_can_access_user_report($user, $course, true)) {
    -    require_capability('report/outline:view', $coursecontext);
    +if (!report_outline_can_access_user_report($user, $course)) {
    +    print_error('nocapability', 'report_outline');
     }
     
     $stractivityreport = get_string('activityreport');
    
  • report/stats/lib.php+13 9 modified
    @@ -78,21 +78,25 @@ function report_stats_can_access_user_report($user, $course) {
         $coursecontext = context_course::instance($course->id);
         $personalcontext = context_user::instance($user->id);
     
    -    if (has_capability('report/stats:view', $coursecontext)) {
    -        return true;
    -    }
    -
    -    if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    -        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
    +    if ($user->id == $USER->id) {
    +        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
                 return true;
             }
    -
    -    } else if ($user->id == $USER->id) {
    -        if ($course->showreports and (is_viewing($coursecontext, $USER) or is_enrolled($coursecontext, $USER))) {
    +    } else if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext)) {
    +        if ($course->showreports and (is_viewing($coursecontext, $user) or is_enrolled($coursecontext, $user))) {
                 return true;
             }
         }
     
    +    // Check if $USER shares group with $user (in case separated groups are enabled and 'moodle/site:accessallgroups' is disabled).
    +    if (!groups_user_groups_visible($course, $user->id)) {
    +        return false;
    +    }
    +
    +    if (has_capability('report/stats:view', $coursecontext)) {
    +        return true;
    +    }
    +
         return false;
     }
     
    
  • report/stats/user.php+1 1 modified
    @@ -51,7 +51,7 @@
         require_login($course);
     }
     
    -if (!report_stats_can_access_user_report($user, $course, true)) {
    +if (!report_stats_can_access_user_report($user, $course)) {
         // this should never happen
         print_error('nocapability', 'report_stats');
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.