飞桨(PaddlePaddle)命令注入漏洞 PDSA-2023-020 深度解析:`_wget_download` 任意命令执行(CVE-2023-52311)
2026/9/13 1:21:11 网站建设 项目流程

飞桨(PaddlePaddle)命令注入漏洞 PDSA-2023-020 深度解析:_wget_download任意命令执行(CVE-2023-52311)

【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle

本文围绕飞桨(PaddlePaddle)安全公告 PDSA-2023-020 展开,剖析paddle.utils.download._wget_download中因直接拼接 URL 调用系统命令导致的命令注入漏洞(CVE-2023-52311),并结合当前仓库源码 python/paddle/utils/download.py 说明其修复方向、防御手法与安全加固实践,帮助读者理解该漏洞成因并掌握排查、复现与防护方法。

漏洞概述

项目内容
安全公告编号PDSA-2023-020
CVE 编号CVE-2023-52311
漏洞类型命令注入(Command Injection)
受影响函数paddle.utils.download._wget_download
影响版本飞桨 2.6.0 之前版本(< 2.6.0)
修复版本飞桨 2.6.0 及之后版本
报告来源huntr.com

该公告完整收录于 security/advisory/pdsa-2023-020_cn.md,同时在 security/README_cn.md 的公告清单中以"Command injection in _wget_download,受影响版本 < 2.6.0"列出,是飞桨 2.6.0 之前版本中已披露的多项安全问题之一。

漏洞根因分析

命令注入的本质

命令注入漏洞的根源在于:程序将外部可控输入(此处为 URL 字符串)直接拼接进系统命令中执行,且未对输入做任何过滤或转义。攻击者只要在 URL 中注入 shell 元字符(如;&|$()、反引号`等),就能让拼接后的命令在原有意图之外执行任意命令。

历史实现中的缺陷点

在旧版本中,_wget_download(url, fullname)的实现方式类似:

def _wget_download(url, fullname): # 旧版本实现(存在漏洞) os.system('wget -O "%s" %s' % (fullname, url))

攻击者传入的url参数未经任何校验直接拼入os.system命令字符串。官方公告给出的 PoC 如下:

from paddle import utils utils.download._wget_download("aa; touch codexecution", "bb")

当执行_wget_download("aa; touch codexecution", "bb")时,实际拼接出的 shell 命令为:

wget -O "bb" aa; touch codexecution

由于;是 shell 的命令分隔符,touch codexecution会被当作独立命令执行,攻击者在服务器上成功创建了codexecution文件,即实现了任意命令执行。若攻击者将注入负载替换为curl ... | sh、反弹 shell 等,则可直接接管运行飞桨的目标机器。

攻击面与可利用性

从源码结构看,download.py模块负责下载预训练模型权重、数据集等资源,其主要入口包括:

  • get_weights_path_from_url(url, md5sum):下载预训练权重,默认存放于~/.cache/paddle/hapi/weights
  • get_path_from_url(url, root_dir, md5sum, ...):通用下载入口,支持wgetget两种方法。

在漏洞版本中,get_path_from_urlmethod参数支持'wget',该路径即调用存在缺陷的_wget_download。因此,凡是能向下载 URL 传入外部输入的场景(例如加载来自第三方 URL 的模型权重、数据集下载、定制化模型库)均可能成为攻击入口,攻击者无需认证即可尝试注入。

修复与当前仓库的防御实现

官方在 commitd5550d3f2f5bab48c783b4986ba1cd8e061ce542(PR #59957)中修复了该问题,修复随飞桨 2.6.0 发布。

移除危险的 wget 调用链

在当前仓库中,_download_methods仅保留'get'一种实现,彻底移除了 wget 调用:

_download_methods = {'get': _get_download}

对应地,_download通过assert method in _download_methods对方法名做白名单校验,同时限制重试次数(DOWNLOAD_RETRY_LIMIT = 3),杜绝了将 URL 拼入 shell 命令的可能。其源码路径为 python/paddle/utils/download.py。

以 Python 原生 HTTP 客户端替代系统命令

修复后的_get_download使用httpx.stream以流式方式下载文件:

def _get_download(url, fullname): fname = osp.basename(fullname) try: with httpx.stream("GET", url, timeout=None, follow_redirects=True) as req: if req.status_code != 200: raise RuntimeError(f"Downloading from {url} failed with code {req.status_code}!") tmp_fullname = fullname + "_tmp" total_size = req.headers.get('content-length') with open(tmp_fullname, 'wb') as f: if total_size: with tqdm(total=(int(total_size) + 1023) // 1024) as pbar: for chunk in req.iter_bytes(chunk_size=1024): f.write(chunk) pbar.update(1) else: for chunk in req.iter_bytes(chunk_size=1024): if chunk: f.write(chunk) shutil.move(tmp_fullname, fullname) return fullname except Exception as e: logger.info(f"Downloading {fname} from {url} failed with exception {e}") return False

关键安全设计如下:

  • 不经过 shell:URL 仅作为httpx请求参数使用,不存在任何系统命令拼接,从根上消除了命令注入面;
  • 临时文件 + 原子替换:先写入fullname + "_tmp",下载完成后shutil.move原子移动到目标路径,避免半成品文件被误用;
  • 状态码校验:非 200 响应直接抛出RuntimeError
  • 下载失败安全返回:任何异常被捕获并记录日志后返回False,由上层_download决定重试。

下载完整性校验:md5 检查

_download循环中持续执行_md5check(fullname, md5sum),仅当文件存在且 md5 一致时才认为下载成功:

while not (osp.exists(fullname) and _md5check(fullname, md5sum)): if retry_cnt < DOWNLOAD_RETRY_LIMIT: retry_cnt += 1 else: raise RuntimeError(f"Download from {url} failed. Retry limit reached") if not _download_methodsmethod: time.sleep(1) continue

_md5check分块(4KB)计算文件哈希并与期望值比对,这不仅能发现下载损坏,也能在一定程度上抵御恶意/被篡改的下载源。

解压环节的路径穿越防护

与命令注入同源的安全加固还体现在解压环节:_safe_extract_tar_safe_extract_zip_safe_extract_member拒绝绝对路径与../路径穿越(target_path.startswith(os.path.abspath(target_dir) + os.sep)校验),并对符号链接、硬链接等特殊文件采取跳过或抛错策略,防止解压炸弹与 symlink 攻击。相关实现见 python/paddle/utils/download.py 中_safe_extract_*系列函数。

影响范围与版本修复

  • 受影响版本:飞桨 2.6.0 之前所有版本;
  • 修复版本:飞桨 2.6.0 及以上(当前仓库version.txt显示为 3.5.0,已包含修复)。

建议所有飞桨用户立即升级至 2.6.0 或更高版本。升级后可通过检查_download_methods中是否仅存在'get'实现,快速确认已移除_wget_download调用链。

安全自查与防护建议

代码自查清单

针对类似"URL/文件名拼入系统命令"的代码模式,建议按以下清单排查:

  1. 是否存在os.systemsubprocess.call/run/Popen且参数由外部输入直接拼接;
  2. 是否对 URL 做过http:///https://协议白名单校验(当前仓库通过 python/paddle/utils/download.py 的is_url做基础检查);
  3. 是否使用参数数组形式传参(subprocess.run(["wget", "-O", fname, url])),避免 shell 解释;
  4. 是否对下载内容做完整性校验(md5/sha256);
  5. 解压路径是否防止路径穿越与符号链接攻击。

运行非信任模型的安全准则

飞桨官方在 SECURITY_cn.md 中明确要求:永远在沙箱中加载和运行非信任模型,并了解其可能造成的影响。这是因为paddle.load底层使用 pickle 反序列化,恶意模型可借此执行任意命令。同理,凡涉及外部 URL 资源加载的功能都应视为可攻击面,建议在隔离环境(容器、虚拟机、专用沙箱)中运行,并遵循最小权限原则。

漏洞上报与致谢

飞桨鼓励负责任地披露(Responsible Disclosure)安全问题,安全研究人员可通过 BSRC 平台提交漏洞,提交时建议包含:

  • 漏洞细节、复现步骤与 PoC;
  • 攻击场景与攻击者能达到的效果;
  • 问题是否已公开及公开情况;
  • 姓名与从属关系(可选公开)。

飞桨会将修复情况注明在版本发布说明中,并在致谢公告中公布漏洞详情与提报人信息。完整安全模型与上报流程见 SECURITY_cn.md 与 security/README_cn.md。本次漏洞由 huntr.com 提交。

延伸阅读

  • PDSA-2023-019:get_online_pass_interval 命令注入:同类"命令注入"问题的另一案例,可对照学习同类漏洞模式;
  • PDSA-2023-005:fs.py 命令注入:受影响版本 < 2.5.0 的同类问题;
  • 安全公告总表:查看全部已披露公告及受影响版本;
  • 安全使用飞桨指南:了解飞桨安全模型、沙箱运行建议与漏洞上报流程。

【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询