{"id":"CVE-2026-115774","kind":"vulnerability","aliases":["CVE-2026-115774","GHSA-2qrf-pqgx-qh9v"],"related":[],"title":"Twisted: Denial of Service in the Twisted IMAP4 Server via a Negative Literal Size in SEARCH","description":"## Summary\n\nAn infinite-loop vulnerability (CWE-835) in the Twisted IMAP4 server lets an authenticated client freeze the whole process with one small command. `twisted.mail.imap4.parseNestedParens` reads an IMAP literal size with `int()` and never rejects a negative value. A negative size such as `{-100}` drives the parser cursor negative, and because a negative Python index wraps around the end of the string instead of raising, the scanning loop never terminates. It runs synchronously on the single-threaded reactor, so one `SEARCH` command holds the reactor at full CPU forever and every other connection stops being served.\n\n## Details\n\nMeeting an inline literal, `parseNestedParens` reads the size between the braces and advances the cursor past the body:\n\n```python\n# twisted/mail/imap4.py  parseNestedParens\nliteralSize = int(s[i + 1 : end])          # no sign check\ncontentStack[-1].append((s[end + 3 : end + 3 + literalSize],))\ni = end + 3 + literalSize                    # negative size -> i goes negative\n```\n\n`int(b\"-100\")` is `-100`, so `i` goes negative; the loop condition `i < len(s)` stays true and `s[i : i + 1]` on a negative index never raises, so the cursor cycles forever. The `_literalStringLimit` guard only covers the synchronizing-literal continuation path, not this inline branch. The call is reached from `select_SEARCH` through `arg_searchkeys`, dispatched only in the selected state, so a valid login and a selected mailbox (any low-privilege account) suffice. It runs in the reactor thread, and the connection timeout cannot fire because the reactor itself is frozen.\n\nAffected product: Twisted (`twisted.mail`), the IMAP4 server `twisted.mail.imap4.IMAP4Server`\nAffected versions: 26.4.0 and earlier\n\n## PoC\n\nLog in, select a mailbox, and send a `SEARCH` whose literal size is negative:\n\n```text\nC: t1 LOGIN testuser password-test\nC: t2 SELECT INBOX\nC: t3 SEARCH {-100}zzzzzzzzzz\n```\n\nThe server never responds and one core stays at 100 percent. In a measured run a health service co-hosted on the same reactor stopped answering the instant the command arrived, showing the freeze hits the whole process and not just the attacking connection. A valid `{5}zzzzz` completes normally and a non-numeric `{abc}` is rejected with a parse error, which isolates the cause to the missing negative-size check.\n\n## Impact\n\nA remote denial of service. One command of about ten bytes freezes the single-threaded reactor, so it stops serving not only every other IMAP connection but every other service co-hosted in the same process (for example SMTP and POP3 under `twistd mail`), and no timeout can recover it until the process is restarted. It requires an authenticated session, so any account holder can trigger it; there is no confidentiality or integrity impact and no code execution.","summary":"Twisted's IMAP4 server freezes its single-threaded process when a logged-in client sends a SEARCH with a negative literal size such as {-100}. An attacker with any valid mail account can stop every service in that process with one short command.","severity":"medium","cvss":4.9,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"Twisted","fixed":["26.10.0rc1"],"affected":"<= 26.4.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-400","cwe-835","imap","package","python","self-hosted","twisted"],"not_affected_if":["auth-required","managed-hosting","no-imap-server"],"how_to_check":"Run pip show Twisted and check whether the version is 26.4.0 or earlier.\nCheck whether the app imports twisted.mail.imap4 or runs twistd mail.","fix":"Upgrade Twisted to 26.10.0rc1 or later (fixed version per sources; check for a final release).\nRestart the process after upgrading if it hung.","status":"open","published_at":"2026-10-23T20:46:12+00:00","first_seen_at":"2026-10-23T20:59:05+00:00","triaged_at":"2026-10-23T21:00:24+00:00","updated_at":"2026-10-23T21:00:24+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T21:00:24Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade Twisted to 26.10.0rc1 or later (fixed version per sources; check for a final release).\nRestart the process after upgrading if it hung.","reach":"network","impact":"medium","summary":"Twisted's IMAP4 server freezes its single-threaded process when a logged-in client sends a SEARCH with a negative literal size such as {-100}. An attacker with any valid mail account can stop every service in that process with one short command.","category":"package","who_cares":["python","twisted","self-hosted","imap"],"confidence":0.8,"needs_auth":"yes","worst_case":"Whole process hangs, stopping all co-hosted mail and other services.","analyst_note":"","how_to_check":"Run pip show Twisted and check whether the version is 26.4.0 or earlier.\nCheck whether the app imports twisted.mail.imap4 or runs twistd mail.\nunknown","needs_analyst":false,"not_affected_if":["no-imap-server","managed-hosting","auth-required"],"user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.000569,"input_tokens":3764,"output_tokens":386,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-1':215C '-100':66B,129C,234C,236C,372C '-115774':19A,27A '-2026':18A,26A '-400':30B '-835':32B,92C '1':209C,253C '100':383C '26.4.0':339C '2qrf':22A '3':219C,221C,225C '5':421C 'abc':430C 'account':73B,308C,513C 'advanc':196C 'affect':328C,337C 'answer':400C 'append':216C 'arg':288C 'around':142C 'arriv':405C 'attack':68B,417C 'authent':100C,509C 'b':233C 'bodi':201C 'brace':194C 'branch':279C 'byte':459C 'c':358C,365C,369C 'call':281C 'cannot':320C 'caus':440C 'check':213C,447C 'client':55B,101C 'co':393C,481C 'co-host':392C,480C 'code':527C 'command':84B,109C,168C,404C,455C 'complet':423C 'condit':243C 'confidenti':521C 'connect':179C,318C,418C,475C 'contentstack':214C 'continu':274C 'core':380C 'cover':269C 'cpu':174C 'cursor':133C,198C,262C 'cve':17A,25A 'cwe':29B,31B,91C 'cycl':263C 'denial':2A,451C 'detail':183C 'dispatch':290C 'drive':130C 'earlier':341C 'end':144C,210C,218C,220C,224C 'error':436C 'everi':76B,177C,472C,477C 'exampl':488C 'execut':528C 'fire':321C 'forev':175C,264C 'freez':44B,102C,408C,460C 'frozen':327C 'full':173C 'ghsa':21A 'ghsa-2qrf-pqgx-qh9v':20A 'goe':230C,239C 'guard':267C 'health':390C 'hit':409C 'hold':169C 'holder':514C 'host':38B,394C,482C 'imap':33B,113C,474C 'imap4':8A,42B,96C,333C 'imap4.imap4server':336C 'impact':448C,524C 'inbox':368C 'index':140C,257C 'infinit':88C 'infinite-loop':87C 'inlin':186C,278C 'instant':402C 'instead':148C 'int':117C,206C,232C 'integr':523C 'isol':438C 'len':245C 'let':98C 'liter':13A,62B,114C,187C,273C,353C 'literals':205C,222C,226C 'literalstringlimit':266C 'log':53B,343C 'logged-in':52B 'login':299C,360C 'loop':89C,153C,242C 'low':306C 'low-privileg':305C 'mail':72B,494C 'mailbox':303C,347C 'measur':387C 'meet':184C 'miss':443C 'negat':12A,61B,122C,125C,134C,138C,227C,231C,240C,256C,356C,445C 'negative-s':444C 'never':119C,154C,258C,376C 'non':428C 'non-numer':427C 'normal':424C 'numer':429C 'one':82B,107C,166C,379C,454C 'packag':34B 'pars':435C 'parsenestedparen':188C,204C 'parser':132C 'password':363C 'password-test':362C 'past':199C 'path':275C 'percent':384C 'poc':342C 'pop3':491C 'pqgx':23A 'privileg':307C 'process':49B,80B,105C,412C,486C,503C 'product':329C 'python':35B,139C,202C 'qh9v':24A 'rais':150C,259C 'reach':283C 'reactor':164C,171C,314C,324C,398C,465C 'read':111C,189C 'recov':499C 'reject':120C,432C 'remot':450C 'requir':507C 'respond':377C 'restart':505C 'run':157C,311C,388C 'scan':152C 'search':16A,58B,167C,286C,351C,371C 'searchkey':289C 'select':285C,294C,302C,345C,367C 'self':37B 'self-host':36B 'send':56B,349C 'serv':182C,469C 'server':9A,43B,97C,334C,375C 'servic':4A,77B,391C,453C,479C 'session':510C 'short':83B 'show':406C 'sign':212C 'singl':47B,162C,463C 'single-thread':46B,161C,462C 'size':14A,63B,115C,126C,191C,228C,354C,446C 'small':108C 'smtp':489C 'state':295C 'stay':247C,381C 'stop':75B,180C,399C,468C 'string':147C 'suffic':309C 'summari':85C 'synchron':158C,272C 'synchronizing-liter':271C 't1':359C 't2':366C 't3':370C 'ten':458C 'termin':155C 'test':364C 'testus':361C 'text':357C 'thread':48B,163C,315C,464C 'timeout':319C,497C 'trigger':516C 'true':248C 'twist':1A,7A,39B,40B,95C,330C 'twistd':493C 'twisted':28A 'twisted.mail':331C,335C 'twisted.mail.imap4.parsenestedparens':110C 'twisted/mail/imap4.py':203C 'valid':71B,298C,420C 'valu':123C 'version':338C 'via':10A 'vulner':90C 'whole':104C,411C 'whose':352C 'wrap':141C 'zzzzz':422C 'zzzzzzzzzz':373C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-23T20:46:12+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-2qrf-pqgx-qh9v","url":"https://github.com/advisories/GHSA-2qrf-pqgx-qh9v","severity":"medium","published_at":"2026-10-23T20:46:12+00:00","modified_at":"2026-10-23T20:46:13+00:00","first_seen_at":"2026-10-23T20:59:05+00:00","fetched_at":"2026-10-23T20:59:05+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-115774","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-115774","severity":null,"published_at":"2026-10-23T21:17:22+00:00","modified_at":"2026-10-23T21:17:22+00:00","first_seen_at":"2026-10-23T21:57:34+00:00","fetched_at":"2026-10-23T22:29:04+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}