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.
| Package | Affected versions | Patched versions |
|---|---|---|
moodle/moodlePackagist | >= 3.3.0, < 3.3.2 | 3.3.2 |
moodle/moodlePackagist | >= 3.2.0, < 3.2.5 | 3.2.5 |
moodle/moodlePackagist | < 3.1.8 | 3.1.8 |
Affected products
45cpe: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:*:*:*:*:*:*:*
Patches
185b531e8beaeMDL-58762 report: Check group permissions in course user reports
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- moodle.org/mod/forum/discuss.phpnvdPatchVendor AdvisoryWEB
- www.securityfocus.com/bid/100848nvdThird Party AdvisoryVDB Entry
- github.com/advisories/GHSA-gw95-48xq-gqf9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-12157ghsaADVISORY
- github.com/moodle/moodle/commit/85b531e8beae3497ec2075e07e59c581fccb317cghsaWEB
- web.archive.org/web/20210124103841/http://www.securityfocus.com/bid/100848ghsaWEB
News mentions
0No linked articles in our index yet.