
Moodle 5.2.3 - High - Remote - IDOR - core_grades_get_gradeitems lists hidden grade items
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · moodle-grades-gradeitems
Moodle LMS 5.2.3 - Moodle
Class: IDOR Reach: Remote
core_grades_get_gradeitems is ajax plus the official mobile service, with no capabilities key in services.php. validate_context is the only gate. Then grade_item::fetch_all dumps every item in the course. The array_filter drops GRADE_TYPE_NONE, category items, and the course item. Hidden is not in that filter. Sibling core_grades_get_grade_tree already requires moodle/grade:viewall.
An enrolled student POSTs {courseid} and gets names of hidden grade items the student gradebook UI still hides.
| ID | no CVE yet |
| CWE | CWE-862 / CWE-200 |
| CVSS | Medium: 4.3 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N |
| Product | Moodle |
| Affected | 5.2.3 tag v5.2.3 (344232c15336c71b80f9aca8359ce0e0a9f3d116) |
| Auth | enrolled student (moodle/grade:view; lacks viewall / viewhidden) |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Writer: an enrolled student in the course. They POST the AJAX service with {courseid}. The response is names, IDs, and categories. The return schema has no scores. Not RCE. Not LPE. Not a score write.
| Privilege | Method / sink | Recon / integrity / availability | Lab-proven or inferred |
|---|---|---|---|
| Read hidden grade-item name, id, and category | POST /lib/ajax/service.php?sesskey= core_grades_get_gradeitems {courseid} | Recon | Lab-proven. HTTP 200, names include MOODLE-GRADES-GRADEITEMS-WITNESS |
| Read visible grade-item name, id, and category | Same response | Recon (intended for teachers) | Lab-proven. names include Visible Lab Assignment |
| Same catalogue over the official mobile service | Same class, services includes MOODLE_OFFICIAL_MOBILE_SERVICE | Recon | Inferred from services.php. Lab used AJAX. |
Walk sequential courseid values for courses the student can enter | Same method, guessed ids | Recon | Inferred. Lab used the seeded course id 2 |
| See the hidden item in the student gradebook UI | GET /grade/report/user/index.php?id= | n/a | Lab-proven closed. hidden-in-ui=False, visible-in-ui=True |
| Call without a session | AJAX with a dummy sesskey | n/a | Lab-proven closed. servicerequireslogin |
| Call as a logged-in user who is not enrolled | outsider AJAX, same courseid | n/a | Lab-proven closed. requireloginerror |
| Read other students' scores or write grades | n/a | n/a | Closed. Return keys are id, itemname, category |
| PHP RCE / OS LPE | n/a | n/a | Closed. This leftover is a catalogue read |
A teacher or manager who already has moodle/grade:viewall already sees the catalogue. Guest / unauthenticated callers still need a course context.
Same pin leftovers: Blackboard img file read, enrol_database SQLi, grades feedback IDOR.
I pinned moodle/moodle v5.2.3 after MSA-26-0042/0043. Grade web services were in that MSA window. get_gradeitems.php still has no require_capability. The student UI already hid the item. The AJAX catalogue did not.
Wrong turns already recorded: treating unauth AJAX 200 as the leak (body is servicerequireslogin); calling this a score IDOR because the method name has grade (schema is names only); expecting get_grade_tree to be the same leftover (it already requires viewall). A reverse shell. Theatre.
Live Moodle v5.2.3 on php:8.3-apache + MariaDB 11.4. Seed enrols labstudent, leaves laboutsider out of the course, and creates a visible manual item plus a hidden item whose name is the witness. run.sh copies a local pin or git clone --depth 1 --branch v5.2.3. The Moodle tree is not in this pack.
python3 moodle-grades-gradeitems-Abraxas-Labs.py
or
cd lab
./run.sh
Target only http://127.0.0.1:19590. Compose project moodle-grades-gradeitems. Stack tears down after the run.
SUCCESS moodle-grades-gradeitems student-ajax=200 hidden-in-ajax=yes hidden-in-ui=no visible-in-ajax=yes visible-in-ui=yes outsider-error=yes unauth-error=yes MOODLE-GRADES-GRADEITEMS-WITNESS
After validate_context, require the same cap as get_grade_tree: require_capability('moodle/grade:viewall', $context). Also drop hidden items unless the caller has moodle/grade:viewhidden. Add a Behat/PHPUnit case: enrolled student, hidden manual item, core_grades_get_gradeitems must omit that item (or 403). Student gradebook UI already hides it.
get_gradeitems.phpget_grade_tree.phpservices.php (core_grades_get_gradeitems, no capabilities)GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.