Lion Cipher Lab
Stop a 'billion laughs' XML entity-expansion denial-of-service
- AppSec / DevSecOps
- Intermediate
- ~45 min
- Free · no account required
Why it matters
The 'billion laughs' attack -- also called an XML bomb -- is one of the cheapest denial-of-service attacks there is: a file a few hundred bytes long can force a naive XML parser to try to build hundreds of millions or billions of characters in memory, freezing or crashing the service that parsed it. It works with no external files and no network at all. It abuses a feature of XML called entity expansion: you declare a small 'internal entity' in the document's DTD, then declare a second entity that expands to ten copies of the first, a third that expands to ten of the second, and so on. Ten…
What you'll learn
- defining the XML vocabulary the attack rides on -- DTD (Document Type Definition), internal entity, and entity expansion -- and how it differs from the EXTERNAL entities used in XXE
- explaining the 'billion laughs' / XML bomb: nested internal entities where each level references ten of the level below, so a tiny file expands to ~10^9 nodes when a naive parser resolves it
- generating a bounded-depth lol.xml with a small Python 3 standard-library script and reading its shape
- MEASURING the blow-up: parsing with xml.dom.minidom at increasing depth and building a table of depth vs expanded characters vs parse time, watching the curve go exponential while the file on disk stays tiny
- knowing that Python's standard-library XML parsers do NOT block this by default, and keeping a hard depth cap so you observe the growth without exhausting memory
- fixing it with defusedxml, which raises EntitiesForbidden on the same file the instant it sees entity definitions -- before expanding anything
- the layered defenses: disable DTD/entity processing outright, and enforce input-size limits and parse timeouts at the boundary as a backstop
- the honest trade-off: size and time limits are blunt and can reject legitimate large documents, so forbidding custom entities is the better default for machine-to-machine XML
Steps in this lab
- What a 'billion laughs' is -- the vocabulary in one breath
- Set up: confirm Python, make a folder, and generate a SMALL bomb
- EXPLOIT (measured): parse it and watch the curve go exponential
- FIX (measured): defusedxml rejects the same file instantly
- Judgment: when to forbid entities outright, and when you cannot