|
| 1 | +--- |
| 2 | +layout: advisory |
| 3 | +title: 'CVE-2026-47241 (net-imap): Net::IMAP: Denial of Service via incomplete raw |
| 4 | + argument validation' |
| 5 | +comments: false |
| 6 | +categories: |
| 7 | +- net-imap |
| 8 | +advisory: |
| 9 | + gem: net-imap |
| 10 | + cve: 2026-47241 |
| 11 | + ghsa: c4fp-cxrr-mj66 |
| 12 | + url: https://www.cve.org/CVERecord?id=CVE-2026-47241 |
| 13 | + title: 'Net::IMAP: Denial of Service via incomplete raw argument validation' |
| 14 | + date: 2026-06-09 |
| 15 | + description: |- |
| 16 | + ### Summary |
| 17 | +
|
| 18 | + Several Net::IMAP commands accept a raw string argument which is only |
| 19 | + validated to prevent CRLF injection and then sent verbatim. If this |
| 20 | + string is derived from user-controlled input, an attacker can force |
| 21 | + the next command to be absorbed as a continuation of the first command. |
| 22 | + This will cause the first command to eventually fail, but also prevents |
| 23 | + it from returning until another command is sent (from another thread). |
| 24 | + That other command will not return until the connection is closed. |
| 25 | +
|
| 26 | + ### Details |
| 27 | +
|
| 28 | + `Net::IMAP::RawData` was hardened in v0.6.4, v0.5.14, and v0.4.24 to |
| 29 | + reject string arguments that would smuggle an invalid literal-continuation |
| 30 | + marker onto the wire (CVE-2026-42257, GHSA-hm49-wcqc-g2xg). But the |
| 31 | + trailing-marker check uses an incorrect regex which does not match |
| 32 | + `{0}` or `{0+}`, so an attacker-controlled seach `criteria` or fetch |
| 33 | + `attr` string ending in `{0}` or `{0+}` passes validation and is sent |
| 34 | + verbatim. Since these arguments are sent as the last argument in the |
| 35 | + command, they will be followed by CRLF. Although the CRLF was intended |
| 36 | + to end the command, the server will interpret it as part of a literal |
| 37 | + prefix. This consumes the next command the client puts on the socket |
| 38 | + as additional arguments to the current command. |
| 39 | +
|
| 40 | + This affects the following command's arguments: |
| 41 | + * `criteria` for `#search` and `#uid_search` |
| 42 | + * `search_keys` for `#sort`, `#thread`, `#uid_sort`, and `#uid_thread` |
| 43 | + * `attr` for `#fetch` and `#uid_fetch` |
| 44 | +
|
| 45 | + The command which contained the attacker's raw data will not be able |
| 46 | + to complete until the _next_ command is issued. If commands are only |
| 47 | + sent from single thread, the first command will hang until the connection |
| 48 | + times out (most likely by the server closing the connection). |
| 49 | +
|
| 50 | + If a second command is sent _(from another thread)_, this would allow |
| 51 | + the server to respond to the first command. This combined command |
| 52 | + _will_ be invalid: |
| 53 | + * The `{0}\\r\\n` literal prohibits other arguments (such as a quoted |
| 54 | + string) from spanning both commands |
| 55 | + * It will be sent without the space delimiter which is required |
| 56 | + between arguments. |
| 57 | + * The second command's tag will not be a valid argument to any of the |
| 58 | + vulnerable commands. |
| 59 | +
|
| 60 | + So the server _should_ respond to the first command with a `BAD` response, |
| 61 | + which will raise a `BadResponseError`. |
| 62 | +
|
| 63 | + But, since the server never saw a second command, the second command will |
| 64 | + never receive a tagged response and the thread that sent it will hang until |
| 65 | + the connection is closed. |
| 66 | +
|
| 67 | + ### Impact |
| 68 | +
|
| 69 | + This will result in unexpected crashes and timeouts, which could be used |
| 70 | + to create a simple denial of service attack. This attack will present |
| 71 | + very similarly to common network issues or server issues which also result |
| 72 | + in commands hanging or unexpectedly raising exceptions. By itself, this |
| 73 | + does not allow command injection. But the confusion caused by these |
| 74 | + errors could lead to other downstream issues, especially in a |
| 75 | + multi-threaded environment. |
| 76 | +
|
| 77 | + ### Mitigation |
| 78 | +
|
| 79 | + Update to a patched version of `net-imap` which validates that `RawData` |
| 80 | + arguments may not end with literal continuation markers. |
| 81 | + If `net-imap` cannot be upgraded:\n* Validate that user input to the |
| 82 | + affected command arguments does not end with `\"}\"`. |
| 83 | + * Use of `Timeout` or other standard strategies for slow connections |
| 84 | + and misbehaving servers will also mitigate the effects of this. |
| 85 | +
|
| 86 | + ### Extra caution is required when issuing commands from multiple threads. |
| 87 | +
|
| 88 | + While `net-imap` does have rudimentary support for issuing commands |
| 89 | + from multiple threads, the user is responsible for synchronizing that |
| 90 | + commands are issued in a logically coherent order, and for ensuring |
| 91 | + that commands are only pipelined when it is safe to do so. |
| 92 | +
|
| 93 | + Practically, this means that many commands cannot be safely pipelined together, |
| 94 | + and user code will often need to wait for state changing commands to successfully |
| 95 | + complete before issuing commands that rely on that state change. |
| 96 | + cvss_v3: 2.1 |
| 97 | + patched_versions: |
| 98 | + - "~> 0.5.15" |
| 99 | + - ">= 0.6.4.1" |
| 100 | + related: |
| 101 | + url: |
| 102 | + - https://www.cve.org/CVERecord?id=CVE-2026-47241 |
| 103 | + - https://rubygems.org/gems/net-imap/versions/0.6.4.1 |
| 104 | + - https://github.com/ruby/net-imap/releases/tag/v0.6.4.1 |
| 105 | + - https://github.com/ruby/net-imap/security/advisories/GHSA-c4fp-cxrr-mj66 |
| 106 | + - https://github.com/advisories/GHSA-c4fp-cxrr-mj66 |
| 107 | + notes: | |
| 108 | + - cve is reserved |
| 109 | + - cvss_v3 - in GHSA ; No cvss_v2, cvss_v4 values. |
| 110 | +--- |
0 commit comments