abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · moodle-grades-gradeitems
Moodle LMS 5.2.3 - Moodle
类别: IDOR 可达性: 远程
core_grades_get_gradeitems 是 ajax 加上官方移动服务,在 services.php 中没有 capabilities 键。validate_context 是唯一的关卡。然后 grade_item::fetch_all 会导出课程中的每一项。array_filter 会丢弃 GRADE_TYPE_NONE、类别项和课程项。隐藏项不在该过滤器中。同类的 core_grades_get_grade_tree 已经要求 moodle/grade:viewall。
已选课的学生 POST {courseid},即可获取学生成绩册 UI 仍然隐藏的隐藏成绩项名称。
| ID | 暂无 CVE |
| CWE | CWE-862 / CWE-200 |
| CVSS | 中危:4.3 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N |
| 产品 | Moodle |
| 受影响版本 | 5.2.3 标签 v5.2.3 (344232c15336c71b80f9aca8359ce0e0a9f3d116) |
| 认证 | 已选课的学生(moodle/grade:view;缺少 viewall / viewhidden) |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1 |
写入者:课程中已选课的学生。他们用 {courseid} POST 该 AJAX 服务。响应是名称、ID 和类别。返回模式中没有分数。不是 RCE。不是 LPE。不是分数写入。
| 权限 | 方法 / 汇聚点 | 侦察 / 完整性 / 可用性 | 实验验证或推断 |
|---|---|---|---|
| 读取隐藏成绩项的名称、id 和类别 | POST /lib/ajax/service.php?sesskey= core_grades_get_gradeitems {courseid} | 侦察 | 实验验证。HTTP 200,名称包含 MOODLE-GRADES-GRADEITEMS-WITNESS |
| 读取可见成绩项的名称、id 和类别 | 同一响应 | 侦察(本意是给教师) | 实验验证。名称包含 Visible Lab Assignment |
| 通过官方移动服务获取同一目录 | 同一类,services 包含 MOODLE_OFFICIAL_MOBILE_SERVICE | 侦察 | 从 services.php 推断。实验使用 AJAX。 |
遍历顺序 courseid 值以获取学生可进入的课程 | 同一方法,猜测 id | 侦察 | 推断。实验使用预置课程 id 2 |
| 在学生成绩册 UI 中看到隐藏项 | GET /grade/report/user/index.php?id= | 不适用 | 实验验证 已关闭。hidden-in-ui=False,visible-in-ui=True |
| 无会话调用 | 使用假 sesskey 的 AJAX | 不适用 | 实验验证 已关闭。servicerequireslogin |
| 以已登录但未选课的用户调用 | 外部人员 AJAX,同一 courseid | 不适用 | 实验验证 已关闭。requireloginerror |
| 读取其他学生的分数或写入成绩 | 不适用 | 不适用 | 已关闭。返回键为 id、itemname、category |
| PHP RCE / OS LPE | 不适用 | 不适用 | 已关闭。此遗留问题为目录读取 |
已经拥有 moodle/grade:viewall 的教师或管理员本就能看到该目录。访客 / 未认证调用者仍需要课程上下文。
同一版本遗留问题:Blackboard img 文件读取、enrol_database SQLi、grades feedback IDOR。
在 MSA-26-0042/0043 之后,我固定了 moodle/moodle v5.2.3。成绩 Web 服务在该 MSA 窗口内。get_gradeitems.php 仍然没有 require_capability。学生 UI 已经隐藏了该项。AJAX 目录却没有。
已记录的弯路:将未认证 AJAX 200 视为泄露(响应体是 servicerequireslogin);因为方法名含有 grade 就称其为分数 IDOR(模式仅含名称);期望 get_grade_tree 是同一遗留问题(它已经要求 viewall)。一个反弹 shell。作秀。
在 php:8.3-apache + MariaDB 11.4 上运行实时 Moodle v5.2.3。种子数据选课 labstudent,将 laboutsider 排除在课程之外,并创建一个可见的手动项以及一个名称即见证字符串的隐藏项。run.sh 复制本地固定版本或 git clone --depth 1 --branch v5.2.3。Moodle 代码树不在此包中。
python3 moodle-grades-gradeitems-Abraxas-Labs.py
或
cd lab
./run.sh
目标 仅 http://127.0.0.1:19590。Compose 项目 moodle-grades-gradeitems。运行后栈会拆除。
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
在 validate_context 之后,要求与 get_grade_tree 相同的权限:require_capability('moodle/grade:viewall', $context)。同时,除非调用者拥有 moodle/grade:viewhidden,否则丢弃隐藏项。添加一个 Behat/PHPUnit 用例:已选课学生、隐藏手动项,core_grades_get_gradeitems 必须省略该项(或返回 403)。学生成绩册 UI 已经隐藏了它。
get_gradeitems.phpget_grade_tree.phpservices.php(core_grades_get_gradeitems,无 capabilities)GNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。无担保。