
Advisory: CVE-2026-38360 path traversal (CWE-22) in dash-uploader (Python/PyPI)
未经身份验证的路径遍历影响 fohrloop/dash-uploader(Python,PyPI),可导致任意文件写入,进而(但不限于)实现 远程代码执行(RCE)、应用程序源代码覆盖、存储型 XSS 以及持久后门安装。
该仓库已于 2025 年 7 月 19 日归档,没有活跃维护者。所有已发布版本(0.1.0 至 0.7.0a2)均受影响且将一直如此。该包每月仍有大约 28,000 次下载。
任何在生产环境中运行 dash-uploader 的人都必须自行应用缓解措施。推荐的修复方案是迁移到 Plotly Dash 内置的 dcc.Upload 组件。完整选项请参见缓解措施。
dash_uploader/httprequesthandler.py 中 request.form.get() 的三个用户可控参数直接传入 os.path.join() 和 os.makedirs(),未经任何清理或验证:
upload_id(第 57 行→第 161 行,BaseHttpRequestHandler.get_temp_root):控制目标目录。攻击者可发送 upload_id=../../../../usr/local/lib/python3.10/site-packages,文件将被写入 Python 的包目录。
resumableFilename(第 51 行→第 108 行,BaseHttpRequestHandler._post):控制最终文件名。攻击者即使使用合法的 upload_id,也可通过文件名遍历出上传目录。
resumableIdentifier(第 54 行→第 64 行,BaseHttpRequestHandler._post):用于通过 os.makedirs() 创建临时目录。攻击者可在文件系统的任何位置创建任意目录。
上传端点(默认为 /API/dash-uploader)无需认证。v0.5.0 中添加的 http_request_handler 钩子允许通过 post_before() 进行请求前检查,但有漏洞的 _post() 方法在钩子返回后直接从 request.form 读取所有参数。钩子无法在库处理之前清理参数。通过钩子添加认证的开发者仍然容易受到来自已认证用户的路径遍历攻击。
# dash_uploader/httprequesthandler.py
def _post(self):
resumableFilename = request.form.get("resumableFilename", default="error", type=str)
resumableIdentifier = request.form.get("resumableIdentifier", default="error", type=str)
upload_id = request.form.get("upload_id", default="", type=str)
...
temp_root = self.get_temp_root(upload_id) # upload_id flows in here
temp_dir = os.path.join(temp_root, resumableIdentifier) # raw user input -> os.path.join
if not os.path.isdir(temp_dir):
os.makedirs(temp_dir) # raw user input -> os.makedirs
def get_temp_root(self, upload_id):
return os.path.join(self.upload_folder, upload_id) # no sanitization: ../../ escapes upload_folder
三个独立的遍历汇点共享同一个根本原因:表单值未经任何验证即进入 os.path.join 和 os.makedirs。
未认证的远程攻击者向上传端点发送 HTTP POST 多部分请求。通过向 upload_id 表单参数中注入路径遍历序列(../),攻击者控制上传文件的目标目录。例如,upload_id=../../../../usr/local/lib/python3.10/site-packages 将文件写入 Python 的包目录,从而通过 .pth 自动执行在下次解释器启动时实现 RCE。
无需认证、会话令牌或 CSRF 令牌。另外两个参数(resumableFilename 和 resumableIdentifier)通过同一端点提供独立的遍历向量。官方快速入门文档中显示的默认库配置可通过单个 curl 命令被利用。
任意文件写入到服务器进程可写的任意目录。这通过几种众所周知的基元可转化为 远程代码执行(RCE)。
RCE 基元
.pth 文件放入 site-packages。在下次解释器启动时执行攻击者提供的代码。sitecustomize.py 或 usercustomize.py。每次 Python 启动时执行。app.wsgi、wsgi.py)。在下次工作进程重载时执行。/etc/cron.d/、/etc/cron.hourly/、用户 crontab 假脱机)——当进程拥有必要权限时。定时执行。/etc/systemd/system/、~/.config/systemd/user/)。在下次服务启动或重启时执行。/etc/ld.so.preload。预加载攻击者代码到后续每个二进制执行中。~/.bashrc、~/.profile、)。在应用用户下次交互登录时执行。Web 层级影响
site-packages 中的 Dash 框架 JS)在主域上实现存储型跨站脚本,影响每个用户的每次页面加载,直到应用程序重启文件系统影响
resumableIdentifier 参数使用 os.makedirs() 在进程可到达的任何位置创建任意目录(可用于 inode 耗尽,或为写入不存在的目录树做准备)dash_uploader/httprequesthandler.pyBaseHttpRequestHandler.get_temp_root()BaseHttpRequestHandler._post()针对当前已部署用户,按优先顺序排列的选项:
dcc.Upload,即 Plotly Dash 随附的官方上传组件。文件以 base64 字符串形式到达回调;不暴露任何写入文件系统的处理程序,也不存在客户端控制的目标路径,因此此处提到的 bug 类别不适用。最适合中小型文件。对于超大文件,请参见选项 2。werkzeug.utils.secure_filename() 和硬编码的服务器端目标目录,自行编写一个小型 Flask 上传处理程序。切勿接受客户端提供的 upload_id、文件名或标识符值作为路径组件。upload_id、resumableFilename 和 resumableIdentifier 使用严格的白名单(例如仅允许 UUID)进行验证,在请求到达库处理程序之前进行重写或拒绝的层级。库的 http_request_handler 钩子不能阻止遍历,因为参数在钩子返回后从 request.form 读取;清理必须在库之上进行。..、编码变体(%2e%2e、..%2f、%2e%2e%2f)或绝对路径。0.6.1(稳定线)。预发布版本扩展至 0.7.0a2。dash。可选依赖:pyyaml。许可证:MIT。Muhammad Fitri Bin Mohd Sultan
| CVE 编号 | CVE-2026-38360(NVD) |
| 漏洞类型 | 路径遍历(CWE-22) |
| CVSS 3.1 | 9.8 / 严重(AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| 产品 | dash-uploader |
| 受影响版本 | 0.1.0 至 0.7.0a2(全部 18 个版本) |
| 修复版本 | 无(项目已于 2025 年 7 月 19 日归档) |
| 攻击向量 | 远程,未认证 |
| 发现者 | Muhammad Fitri Bin Mohd Sultan |
| 分配方 | MITRE,2026 年 5 月 7 日 |
| 相关 | CVE-2026-38361(同一库中的拒绝服务) |
~/.bash_profile~/.ssh/authorized_keys。以应用用户身份授予对主机的持久 SSH 访问。| 日期 | 事件 |
|---|
| 2026-03-17 | 在生产部署的安全研究期间发现该漏洞。 |
| 2026-03-19 | 向 MITRE 提交 CVE 请求。 |
| 2026-05-07 | MITRE 分配 CVE-2026-38360。 |
| 2026-05-07 | 发布公开通告。 |
| 2026-05-09 | CVE 记录发布于 MITRE CVE 数据库 和 NVD。 |