a1ohadance/CVE-2026-38361
GitHub: a1ohadance/CVE-2026-38361
针对已归档的 Python 库 dash-uploader 中多个未经身份验证的拒绝服务漏洞(CVE-2026-38361)的公开安全公告,详述了 OOM 崩溃、文件截断、磁盘耗尽和大小限制绕过四种攻击方式及其缓解措施。
Stars: 0 | Forks: 0
# CVE-2026-38361:dash-uploader 中多个未经身份验证的 DoS 漏洞
[](https://www.cve.org/CVERecord?id=CVE-2026-38361)
[](https://nvd.nist.gov/vuln/detail/CVE-2026-38361)
[](https://cwe.mitre.org/data/definitions/400.html)
[](https://cwe.mitre.org/data/definitions/670.html)
[](#)
[](#缓解措施)
[](#攻击向量)
[](https://pypi.org/project/dash-uploader/)
[](https://pepy.tech/project/dash-uploader)
[](https://pepy.tech/project/dash-uploader)
[](https://pypi.org/project/dash-uploader/)
[`fohrloop/dash-uploader`](https://github.com/fohrloop/dash-uploader) (Python, PyPI) 中存在多个未经身份验证的**拒绝服务** 问题,包括(但不限于)内存溢出 (OOM) 进程崩溃、文件截断为零字节、永久性磁盘耗尽,以及完全绕过文档中所描述的 `max_file_size` 限制。通过同一组未经过滤的参数,还存在其他资源滥用路径。
### ⚠️ 没有可用的补丁,并且将来也永远不会发布
该代码库已于 [2025-07-19 被归档](https://github.com/fohrloop/dash-uploader/issues/153),目前没有活跃的维护者。所有已发布的版本(从 `0.1.0` 到 `0.7.0a2`)均受影响,并将持续如此。该软件包每月仍有约 28,000 次下载。
任何在生产环境中运行 `dash-uploader` 的用户都必须自行采取缓解措施。推荐的修复方案是迁移到 Plotly Dash 内置的 `dcc.Upload` 组件。有关完整的解决方案,请参阅[缓解措施](#mitigation)。
| | |
|---|---|
| **CVE ID** | [CVE-2026-38361](https://www.cve.org/CVERecord?id=CVE-2026-38361) ([NVD](https://nvd.nist.gov/vuln/detail/CVE-2026-38361)) |
| **漏洞类型** | 不受控制的资源消耗 (CWE-400),始终不正确的控制流实现 (CWE-670) |
| **CVSS 3.1** | 7.5 / 高危 (`AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H`) |
| **产品** | dash-uploader |
| **受影响版本** | `0.1.0` 到 `0.7.0a2`(所有 18 个发行版) |
| **修复版本** | 无(项目已于 2025-07-19 归档) |
| **攻击向量** | 远程,无需身份验证 |
| **发现者** | Muhammad Fitri Bin Mohd Sultan |
| **分配方** | MITRE, 2026-05-07 |
| **相关** | [CVE-2026-38360](https://github.com/a1ohadance/CVE-2026-38360)(同一库中的路径遍历漏洞) |
## 漏洞描述
`dash-uploader` HTTP 处理程序接受未经身份验证的 POST 请求,其中包含攻击者控制的参数,这些参数会直接流入内存分配、文件操作和目录创建过程,期间没有边界检查、速率限制或清理机制。同一段代码执行路径中存在四个独立的问题:
### 1. OOM 崩溃(已验证)
在一台 7.7 GB 内存系统上验证:5 个并发 POST 请求(带有 `resumableTotalChunks=30000000`)在 2 秒内触发了 Linux OOM killer。内核日志确认如下:
```
Out of memory: Killed process 24203 (python3) total-vm:8302276kB, anon-rss:7068012kB
```
每个请求通过针对 `range(1, resumableTotalChunks + 1)` 的列表推导式分配了大约 2.9 GB 内存。服务进程被终止,应用程序完全不可用,直到手动重启。
### 2. 文件截断(已验证)
一个包含 42 字节数据的文件,通过一个带有 `resumableTotalChunks=0` 的 POST 请求被缩减为 0 字节。根本原因是 Python 的 `all()` 对空可迭代对象会返回 `True`,这诱使上传处理程序将零分块视为上传完成。现有文件通过 `os.unlink()` 被删除并替换为空文件。
### 3. 废弃上传文件累积(已验证)
创建了 10 个包含分块文件的孤立临时目录,并在磁盘上无限期保留。在所有源文件的代码库范围内搜索 `cleanup`、`ttl`、`expire`、`garbage`、`purge`、`cron`、`schedule` 和 `periodic`,均未返回任何结果。唯一的清理调用 (`shutil.rmtree`) 仅在上传完成时执行。没有任何机制可以回收未完成会话所占用的磁盘空间。
### 4. 绕过 `max_file_size`(已验证)
服务器接受了一个 5 MB 的分块上传请求,而该文件声称的 `resumableTotalSize=999999999999`(约 999 GB),并返回 HTTP 200。`max_file_size` 参数仅传递给 React JavaScript 组件。服务器从不检查文件大小、分块大小、`Content-Length` 或 Flask 的 `MAX_CONTENT_LENGTH`。开发者设置的 `max_file_size=10` 没有任何服务器端的保护。
## 漏洞代码
```
# dash_uploader/httprequesthandler.py
def _post(self):
resumableTotalChunks = request.form.get("resumableTotalChunks", type=int) # attacker-controlled, no bounds
...
chunk_paths = [
os.path.join(temp_dir, get_chunk_name(resumableFilename, x))
for x in range(1, resumableTotalChunks + 1) # unbounded; e.g. 30M -> ~2.9 GB -> OOM
]
upload_complete = all([os.path.exists(p) for p in chunk_paths]) # all([]) is True -> truncation when chunks=0
if upload_complete:
target_file_name = os.path.join(temp_root, resumableFilename)
if os.path.exists(target_file_name):
os.unlink(target_file_name) # existing file deleted
with open(target_file_name, "ab") as target_file:
for p in chunk_paths: # empty list -> empty file written
...
```
同一段代码路径既导致了 OOM(巨大的 `resumableTotalChunks`),又导致了文件截断原语(`resumableTotalChunks=0`)。
## 攻击向量
攻击者向 `/API/resumable` 端点发送未经身份验证的 POST 请求。
- **OOM 崩溃:** 5 个带有 `resumableTotalChunks=30000000` 的并发请求,每个分配约 2.9 GB 内存,从而触发 OOM killer。
- **磁盘耗尽:** 启动上传但永不完成;孤立的临时文件会永远累积。
- **文件截断:** 发送 `resumableTotalChunks=0`;Python `all([])=True` 诱使服务器用空内容覆盖目标文件。
- **大小限制绕过:** 开发者设置的任何大小限制仅在客户端 JavaScript 中强制执行,因此直接发送 HTTP 请求即可完全绕过它。
无需任何身份验证或权限。
## 影响范围
- 由单个用户控制的 POST 参数 (`resumableTotalChunks`) 引发无限制的内存分配,进而触发 Linux OOM killer,导致服务进程崩溃
- 由于未完成的上传会话永不清理(代码库中不存在 TTL、垃圾回收或过期机制),从而导致永久性磁盘耗尽
- 通过未经过滤的 `resumableIdentifier` 使用 `os.makedirs()` 创建任意深度的目录,导致文件系统 inode 耗尽
- 当 `resumableTotalChunks=0` 时,由于 Python `all()` 对空可迭代对象返回 `True`,导致文件被截断为零字节,进而造成数据破坏
- 绕过所有文件大小限制,因为 `max_file_size` 仅在客户端 JavaScript 中强制执行,而服务器端处理程序执行零次大小验证,并且从不设置 Flask `MAX_CONTENT_LENGTH`
## 受影响组件
- `dash_uploader/httprequesthandler.py`(`BaseHttpRequestHandler._post` 方法)
- `dash_uploader/upload.py`(`Upload` 函数,`max_file_size` 参数)
- `dash_uploader/configure_upload.py`(缺失 `MAX_CONTENT_LENGTH`)
## 缓解措施
### ⚠️ 无可用补丁,且项目已归档
针对当前已部署用户的解决方案,按优先顺序排列:
1. **迁移到 `dcc.Upload`**,这是 Plotly Dash 官方提供的上传组件。它没有分块计数参数,没有磁盘上的临时状态,并且遵循 Flask `MAX_CONTENT_LENGTH`。此处提到的四个问题均不适用于它。最适合中小型文件。对于超大文件的上传,请参见第 2 项。
2. **自行编写一个小型的 Flask 上传处理程序**,包含明确的按请求大小限制(`MAX_CONTENT_LENGTH`)、对客户端提供的任何分块数量的边界限制,以及针对可接受文件名的白名单。
3. **如果继续使用 dash-uploader**,请在应用程序层面设置 Flask `MAX_CONTENT_LENGTH`(该库并未设置),并在应用程序层或反向代理层拒绝满足以下任一条件的输入:
- `resumableTotalChunks <= 0`
- `resumableTotalChunks` 超过合理界限(例如 10,000)
- `resumableTotalSize` 超过开发者配置的 `max_file_size`
4. **添加速率限制**,在反向代理或 WAF 层对上传端点进行限制,以缓解并发请求导致的 OOM 攻击向量。
5. **定期清理孤立的临时目录**,通过外部 cron 作业执行,因为该库没有内部清理机制。
## 披露时间线
| 日期 | 事件 |
|---|---|
| 2026-03-19 | 在对生产部署进行安全研究期间发现了漏洞。 |
| 2026-03-22 | 向 MITRE 提交了 CVE 申请。 |
| 2026-05-07 | CVE-2026-38361 由 MITRE 分配。 |
| 2026-05-07 | 发布公开安全公告。 |
| 2026-05-09 | CVE 记录在 [MITRE CVE 数据库](https://www.cve.org/CVERecord?id=CVE-2026-38361) 和 [NVD](https://nvd.nist.gov/vuln/detail/CVE-2026-38361) 上发布。 |
## 包上下文
- PyPI 上每月下载量约为 28,000 次(在 2026-05-07 之前的 30 天内为 27,756 次,尽管代码库已归档,但每日下载量仍保持稳定)。来源:[pypistats.org](https://pypistats.org/packages/dash-uploader)。
- 最新发布版本:`0.6.1`(稳定版线)。预发布版本延伸至 `0.7.0a2`。
- 必需依赖项:`dash`。可选依赖项:`pyyaml`。许可证:MIT。
- 11 个依赖包,6 个依赖代码库。
- 153 个 GitHub Stars。
- 代码库已于 2025-07-19 归档([Issue #153](https://github.com/fohrloop/dash-uploader/issues/153))。
- 无先前的 CVE 记录(已于 2026-03-19 对照 NVD、GitHub Advisory Database、Snyk 和 OSV 进行了验证)。
## 参考资料
- https://www.cve.org/CVERecord?id=CVE-2026-38361
- https://nvd.nist.gov/vuln/detail/CVE-2026-38361
- https://github.com/fohrloop/dash-uploader
- https://github.com/fohrloop/dash-uploader/blob/stable/dash_uploader/httprequesthandler.py
- https://github.com/fohrloop/dash-uploader/issues/153
- https://pypi.org/project/dash-uploader/
- https://pypistats.org/packages/dash-uploader
- https://libraries.io/pypi/dash-uploader
- https://pepy.tech/project/dash-uploader
- https://cwe.mitre.org/data/definitions/400.html
- https://cwe.mitre.org/data/definitions/670.html
- https://docs.python.org/3/library/functions.html#all
## 发现者
Muhammad Fitri Bin Mohd Sultan
标签:CVE-2026-38361, CWE-400, CWE-670, Dash, dash-uploader, DoS, OOM, PyPI, Python, Web安全, 内存耗尽, 开源组件漏洞, 拒绝服务攻击, 文件上传漏洞, 无后门, 未授权攻击, 漏洞分析, 漏洞通报, 磁盘耗尽, 网络安全, 蓝队分析, 资源消耗攻击, 路径探测, 软件供应链安全, 远程方法调用, 逆向工具, 配置错误, 隐私保护, 零日漏洞, 高危漏洞