Pre-Auth RCE in Moodle Part II - Session Hijack in Moodle's Shibboleth

Title

Pre-Auth RCE in Moodle Part II - Session Hijack in Moodle's Shibboleth

Product

Moodle

Vulnerable Version

3.11 to 3.11.2, 3.10 to 3.10.6, 3.9 to 3.9.9

Fixed Version

>= 3.11.3, 3.10.7, 3.9.10

Impact

Critical

CVE Number

CVE-2021-40691

In our previous blogpost we have introduced a pre-auth RCE in Moodles Shibboleth plugin. This RCE could be triggered when Moodle was configured to store sessions in individual files which is the default configuration for new installations. However, Moodle also supports storing sessions in the database. In this second part, we reveal a session hijack vulnerability exploiting the over-usage of PHP’s session_decode function, when the database session handler is configured.

1
2
3
4
5
6
7
function LogoutNotification($spsessionid) {
    $sessionclass = \core\session\manager::get_handler_class();
    switch ($sessionclass) {
        case '\core\session\file':
            return \auth_shibboleth\helper::logout_file_session($spsessionid);
        case '\core\session\database':
            return \auth_shibboleth\helper::logout_db_session($spsessionid);

By exploiting this session hijack vulnerability, an unauthenticated attacker can take over the session of any user if shibboleth authentication is enabled. To leverage this vulnerability into a fully blown RCE an active admin session is required. With an admin session it is then trivial to gain RCE.

Log Out to Log In

For every logout request received via the SOAP endpoint /auth/shibboleth/logout.php the function logout_db_session() is invoked. This function iterates over all available sessions in the database and throws every session into the session_decode function. This will decode the serialized session data from the database, and populate the $_SESSION superglobal with the decoded data. As a result, the attacker is logged in as every user with an active session for a fraction of a second.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public static function logout_db_session($spsessionid) {
global $CFG, $DB;

$sessions = $DB->get_records_sql(
    'SELECT userid, sessdata FROM {sessions} WHERE timemodified > ?',
    array(time() - $CFG->sessiontimeout)
);

foreach ($sessions as $session) {
    // Get user session from DB.
    if (session_decode(base64_decode($session->sessdata))) {
        // ....
	if (isset($usersession['SESSION']) && isset($usersession['SESSION']->shibboleth_session_id)) {
        // If there is a match, kill the session.
        if ($usersession['SESSION']->shibboleth_session_id == trim($spsessionid)) {
            // Delete this user's sessions.
            \core\session\manager::kill_user_sessions($session->userid);
        }
    }
}

The problem here is, that the last session is never unloaded and the $_SESSION variable remains populated with the session information of the very last user. This session is now assigned to the attacker’s session cookie due to session_decode. This means that the attacker can refresh the page resulting in the hijack of a seemingly random user session.

In case the session is not a privileged admin session, the attackers can use the ordinary logout functionality of Moodle to remove this session from the database and repeat the attack. This way, attackers can iterate themselves through the list of all sessions and wait for an admin session to pop up in order to gain trivial Remote Command Execution e.g. via the plugin installer of Moodle.

Recommendation

It was recommended to mitigate this vulnerability by not relying on session_decode in order to identify the session to be logged out. Instead, the session could be identified directly by using the session_id database field provided by Moodle. By doing so, the session to be logged out can be identified and deleted directly with a single database query instead of fetching and iterating all available sessions and relying on session_decode.

Conclusion

In this series two vulnerabilities have been presented that reside in Moodle’s Shibboleth authentication plugin. Both of them stem from the attempts to re-implement or mess with PHP’s internal session mechanisms. This is generally not advisable, due to the complexity and pitfalls that are illustrated within this series. The commits show that these issues come from an almost ancient era. They could have eventually been noticed if source code audits would have been performed that include legay code in their analysis.

It is worth noting that the reporting process was extremely tedious due to problems when understanding and reproducing the issue on BugCrowd’s side 💩. It took 4 Months before the report was finally triaged and relayed to Moodle. For this reason, it is advisable to report security issues directly to Moodle in order to minimize the window of opporturnity for attackers.

Timeline

Date Action
2021-02-21 Submitted Bug via Bugcrowd
2021-06-16 Bugcrowd triaged our report with P1
2021-09-12 Patch released on GitHub