Twisted: Unauthenticated Remote Denial of Service in the Twisted words domish XML Element Stream via an Unbounded Element Tree
CVE-2026-115775 · GHSA-3476-c5c2-fr73 · published 23 Oct 2026
How serious is it
- Severity: high, CVSS score 8.7 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-115775?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
Twisted(PyPI) <= 26.4.0
How to check
- Run
pip show Twistedin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
Twistedto 26.10.0rc1 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## Summary
An allocation-without-limits vulnerability in the Twisted XML element stream lets any unauthenticated remote peer exhaust server memory over a single stream. When the peer opens a container element and streams child elements without ever closing the container, every element is materialised into an in-memory tree with no limit on depth or child count. An element is released only when its end tag arrives, so a container the attacker never closes retains everything beneath it until the process is killed.
## Details
In `ExpatElementStream._onStartElement`, each start tag appends a new element to the current element's children and descends into it, with no depth or child-count check:
```python
# src/twisted/words/xish/domish.py (ExpatElementStream._onStartElement)
if self.documentStarted == 1:
if self.currElem != None:
self.currElem.children.append(e) # append child: no depth/count cap
e.parent = self.currElem
self.currElem = e # descend into the new element
```
An element is emitted and dropped only when its end tag arrives while it sits directly under the document root, so a container that is never closed keeps every child beneath it. The transport `XmlStream.dataReceived` feeds bytes to `self.stream.parse(data)` with no size limit, and in XMPP the stream is parsed during negotiation before authentication.
Affected product: Twisted, `twisted.words.xish.domish` element stream
Affected versions: confirmed against 26.4.0
## PoC
On any endpoint that parses untrusted XML streams with this stack, such as an XMPP server, open a stream by sending the opening `stream:stream` tag and a container element, then stream child elements continuously and never send the container's closing tag. No credentials are required, because in XMPP the stream is parsed before authentication.
Every child is appended to the container and retained because the container is never emitted, and the transport applies no limit on the size of the unfinished document. Because each small element becomes a full object in memory the footprint is many times the traffic sent, so a modest-bandwidth peer, or several such streams, drives the process to memory exhaustion.
## Impact
This is an unauthenticated remote denial of service by memory exhaustion. A single stream grows an in-memory element tree without bound, amplified by per-element object overhead, until the operating system kills the server. There is no confidentiality or integrity impact and no code execution. The affected surface is any deployment that parses untrusted XML streams with this stack, such as an XMPP server or component.
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/twisted/twisted/security/advisories/GHSA-3476-c5c2-fr73
- github.com/twisted/twisted/pull/12751
- github.com/twisted/twisted/commit/8873814bec8288e2466fad4d2c4d64039beb88b0
- github.com/twisted/twisted/releases/tag/twisted-26.10.0rc1
- github.com/advisories/GHSA-3476-c5c2-fr73
- github.com/twisted/twisted
Merged from GitHub Advisory Database, OSV. Last updated 26 Oct 2026. All vulnerabilities · RSS · JSON