Closed Bug 2088499 Opened 11 days ago Closed 10 days ago

IMAP COMPRESS=DEFLATE permits a 1,000x response-line decompression bomb

Categories

(MailNews Core :: Networking: IMAP, defect)

Thunderbird 161
defect

Tracking

(Not tracked)

RESOLVED FIXED
161 Branch

People

(Reporter: tjddbdud1, Assigned: olav)

References

Details

Attachments

(1 file, 1 external file)

Summary

Thunderbird enables IMAP COMPRESS=DEFLATE by default when a configured server advertises it. A malicious or compromised IMAP server can send a very small raw-DEFLATE stream that expands to a response without an LF. nsMsgCompressIStream incrementally inflates the stream, while nsMsgLineStreamBuffer::ReadNextLine() repeatedly grows its backing allocation until a line terminator appears. No decompressed logical-line size limit is enforced.

On the latest official Windows Daily, a 65,237-byte compressed response expanded to 64 MiB and raised resident memory by 119.39 MiB in about 2.4 seconds. The stream can be extended to exhaust available memory and make Thunderbird unresponsive or terminate it.

Security impact

A malicious IMAP provider, a compromised configured server, or an attacker who controls the account's IMAP endpoint can cause a disproportionate memory denial of service during an ordinary authenticated connection. Compression is enabled by default; after initial account configuration, normal background connection or folder expansion is sufficient to reach the vulnerable path.

Suggested severity: Moderate

  • CWE-409 (Improper Handling of Highly Compressed Data / Zip Bomb)
  • CWE-400 / CWE-770 (Uncontrolled Resource Consumption / Allocation Without Limits)
  • CVSS 3.1: AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H = 6.5

Tested build

  • Thunderbird Daily 161.0a1, Windows x64
  • Build ID: 20261103100812
  • Source stamp: c5b3416d323259549cb0d4e99ec64380ad45b697

This build includes the recent IMAP compression-transition fix from Bug 2087454. That fix feeds already-buffered compressed bytes into the inflater, but it does not impose a limit on the decompressed logical response line.

Reproduction

The attached zip contains a production-path xpcshell PoC, runner, source notes, raw logs, and exact measurements.

  1. Run .\run.ps1 -MiB 32.
  2. Observe that Thunderbird authenticates, requests COMPRESS DEFLATE, and processes a 32,623-byte stream which expands to a 32 MiB unterminated logical line.
  3. Run .\run.ps1 -MiB 64.
  4. Observe 65,237 bytes expanding to a 64 MiB unterminated logical line and a 119.39 MiB increase in resident memory.
Expanded response Compressed bytes Ratio Baseline RSS Peak RSS RSS growth
32 MiB 32,623 1,028.55x 57.71 MiB 122.39 MiB 64.69 MiB
64 MiB 65,237 1,028.69x 57.48 MiB 176.87 MiB 119.39 MiB

Both included runs pass assertions that Thunderbird negotiated compression, the expansion ratio exceeds 500x, and resident-memory growth is material.

Root cause

  1. mail.server.default.use_compress_deflate is true.
  2. After login, nsImapProtocol automatically sends COMPRESS DEFLATE when the capability is present.
  3. nsMsgCompressIStream inflates raw DEFLATE into 16 KiB output chunks.
  4. nsImapProtocol::CreateNewLineFromSocket() passes the decompressed stream to nsMsgLineStreamBuffer::ReadNextLine().
  5. When no LF is present, ReadNextLine() repeatedly reallocates its backing buffer and appends decompressed chunks with no protocol-appropriate limit.

A timeout does not help because the server keeps delivering compressed data and the decompressor keeps producing bytes.

Nearby public bugs reviewed

  • Bug 2087454 concerns compressed bytes already present at the compression transition bypassing the inflater and desynchronizing responses. It does not cap decompressed output or line length.
  • Bug 1581017 concerns growth/crashes with unusually large ordinary IMAP responses and was closed WORKSFORME. It does not demonstrate a small compressed input producing ~1,000x output on the latest fixed compression path.
  • Bug 1294074 is an old zlib crash signature associated with IMAP compression, not an attacker-controlled decompressed logical-line resource limit.

Suggested fix

Enforce a conservative maximum decompressed IMAP logical-line length before growing the line buffer, and terminate the connection with a protocol error when the limit is exceeded. The limit must be applied after decompression.

Please keep this report and attachment confidential until triage and a fix are complete.

AI assistance was used to help review source and prepare the test and report. All measurements were produced locally against the official build and the raw logs are attached.

Not much of a security issue, but we should probably fix it

Group: mail-core-security
See Also: → 2087454

I'll take this one, since it's in the same code as bug 2087454.

Plan: give nsMsgLineStreamBuffer an optional maximum line length that IMAP sets, so POP3, NNTP and mailbox are unaffected. When a line exceeds the limit, or the buffer can't grow, ReadNextLine() returns an error, and IMAP closes the connection through its existing error path.

That also fixes a related problem: today, when GrowBuffer() fails or the 4 GB cap is reached, ReadNextLine() returns nullptr without setting *prv, so CreateNewLineFromSocket() keeps looping (while (!newLine && NS_SUCCEEDED(rv) ...)).

Magnus, is 64 MiB OK as the limit? Server response lines stay far below that (a SEARCH over a million messages is about 8 MB). Message body lines from FETCH literals go through the same buffer, though, so the limit has to leave room for broken messages with unwrapped base64.

Assignee: nobody → olav

Yeah 64MiB seems ok for a default limit. Perhaps make it a pref, something like mailnews.max_response_line_length?

nsMsgLineStreamBuffer grew its buffer until a line end arrived, so a small COMPRESS=DEFLATE stream could expand into a line of gigabytes. IMAP now sets a limit from the new pref mailnews.max_response_line_length (64 MiB), and ReadNextLine() fails with NS_ERROR_ILLEGAL_INPUT beyond it, which closes the connection.

ReadNextLine() also reports a failure to grow the buffer instead of returning nullptr with success, which made CreateNewLineFromSocket() loop.

Signed-off-by: Olav Seyfarth <olav@seyfarth.de>

Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Target Milestone: --- → 161 Branch

Pushed by kaie@kuix.de:
https://hg.mozilla.org/comm-central/rev/09d0063ce6cd
Limit the length of IMAP response lines. r=mkmelin

Status: ASSIGNED → RESOLVED
Closed: 10 days ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: