diff --git a/gems/net-imap/CVE-2025-25186.yml b/gems/net-imap/CVE-2025-25186.yml new file mode 100644 index 0000000000..edc669522c --- /dev/null +++ b/gems/net-imap/CVE-2025-25186.yml @@ -0,0 +1,157 @@ +--- +gem: net-imap +cve: 2025-25186 +ghsa: 7fc5-f82f-cx69 +url: https://github.com/ruby/net-imap/security/advisories/GHSA-7fc5-f82f-cx69 +title: Possible DoS by memory exhaustion in net-imap +date: 2025-02-10 +description: | + ### Summary + + There is a possibility for denial of service by memory exhaustion in + `net-imap`'s response parser. At any time while the client is + connected, a malicious server can send can send highly compressed + `uid-set` data which is automatically read by the client's receiver + thread. The response parser uses `Range#to_a` to convert the + `uid-set` data into arrays of integers, with no limitation on the + expanded size of the ranges. + + ### Details + + IMAP's `uid-set` and `sequence-set` formats can compress ranges of + numbers, for example: `"1,2,3,4,5"` and `"1:5"` both represent the + same set. When `Net::IMAP::ResponseParser` receives `APPENDUID` or + `COPYUID` response codes, it expands each `uid-set` into an array of + integers. On a 64 bit system, these arrays will expand to 8 bytes + for each number in the set. A malicious IMAP server may send + specially crafted `APPENDUID` or `COPYUID` responses with very large + `uid-set` ranges. + + The `Net::IMAP` client parses each server response in a separate + thread, as soon as each responses is received from the server. + This attack works even when the client does not handle the + `APPENDUID` or `COPYUID` responses. + + Malicious inputs: + + ```ruby + # 40 bytes expands to ~1.6GB: + "* OK [COPYUID 1 1:99999999 1:99999999]\r\n" + + # Worst *valid* input scenario (using uint32 max), + # 44 bytes expands to 64GiB: + "* OK [COPYUID 1 1:4294967295 1:4294967295]\r\n" + + # Numbers must be non-zero uint32, but this isn't validated. Arrays + # larger than UINT32_MAX can be created. For example, the following + # would theoretically expand to almost 800 exabytes: + "* OK [COPYUID 1 1:99999999999999999999 1:99999999999999999999]\r\n" + ``` + + Simple way to test this: + ```ruby + require "net/imap" + + def test(size) + input = "A004 OK [COPYUID 1 1:#{size} 1:#{size}] too large?\n" + parser = Net::IMAP::ResponseParser.new + parser.parse input + end + + test(99_999_999) + ``` + + ### Fixes + + #### Preferred Fix, minor API changes + + Upgrade to v0.4.19, v0.5.6, or higher, and configure: + + ```ruby + # globally + Net::IMAP.config.parser_use_deprecated_uidplus_data = false + # per-client + imap = Net::IMAP.new(hostname, ssl: true, + parser_use_deprecated_uidplus_data: false) + imap.config.parser_use_deprecated_uidplus_data = false + ``` + + This replaces `UIDPlusData` with `AppendUIDData` and `CopyUIDData`. + These classes store their UIDs as `Net::IMAP::SequenceSet` objects + (_not_ expanded into arrays of integers). Code that does not handle + `APPENDUID` or `COPYUID` responses will not notice any difference. + Code that does handle these responses _may_ need to be updated. See + the documentation for + [UIDPlusData](https://ruby.github.io/net-imap/Net/IMAP/UIDPlusData.html), + [AppendUIDData](https://ruby.github.io/net-imap/Net/IMAP/AppendUIDData.html) + and [CopyUIDData](https://ruby.github.io/net-imap/Net/IMAP/CopyUIDData.html). + + For v0.3.8, this option is not available. + For v0.4.19, the default value is `true`. + For v0.5.6, the default value is `:up_to_max_size`. + For v0.6.0, the only allowed value will be `false` _(`UIDPlusData` + will be removed from v0.6)_. + + #### Mitigation, backward compatible API + + Upgrade to v0.3.8, v0.4.19, v0.5.6, or higher. + + For backward compatibility, `uid-set` can still be expanded + into an array, but a maximum limit will be applied. + + Assign `config.parser_max_deprecated_uidplus_data_size` to set the + maximum `UIDPlusData` UID set size. When + `config.parser_use_deprecated_uidplus_data == true`, larger sets will crash. + When `config.parser_use_deprecated_uidplus_data == :up_to_max_size`, + larger sets will use `AppendUIDData` or `CopyUIDData`. + + For v0.3,8, this limit is _hard-coded_ to 10,000, and larger sets + will always raise `Net::IMAP::ResponseParseError`. + For v0.4.19, the limit defaults to 1000. + For v0.5.6, the limit defaults to 100. + For v0.6.0, the limit will be ignored _(`UIDPlusData` will be + removed from v0.6)_. + + #### Please Note: unhandled responses + + If the client does not add response handlers to prune unhandled + responses, a malicious server can still eventually exhaust all + + client memory, by repeatedly sending malicious responses. However, + `net-imap` has always retained unhandled responses, and it has always + been necessary for long-lived connections to prune these responses. + _This is not significantly different from connecting to a trusted + server with a long-lived connection._ To limit the maximum number + of retained responses, a simple handler might look something like + the following: + + ```ruby + limit = 1000 + imap.add_response_handler do |resp| + next unless resp.respond_to?(:name) && resp.respond_to?(:data) + name = resp.name + code = resp.data.code&.name if resp.data.respond_to?(:code) + if Net::IMAP::VERSION > "0.4.0" + imap.responses(name) { _1.slice!(0...-limit) } + imap.responses(code) { _1.slice!(0...-limit) } + else + imap.responses(name).slice!(0...-limit) + imap.responses(code).slice!(0...-limit) + end + end + ``` +cvss_v3: 6.5 +unaffected_versions: + - "< 0.3.2" +patched_versions: + - "~> 0.3.8" + - "~> 0.4.19" + - ">= 0.5.6" +related: + url: + - https://nvd.nist.gov/vuln/detail/CVE-2025-25186 + - https://github.com/ruby/net-imap/security/advisories/GHSA-7fc5-f82f-cx69 + - https://github.com/ruby/net-imap/commit/70e3ddd071a94e450b3238570af482c296380b35 + - https://github.com/ruby/net-imap/commit/c8c5a643739d2669f0c9a6bb9770d0c045fd74a3 + - https://github.com/ruby/net-imap/commit/cb92191b1ddce2d978d01b56a0883b6ecf0b1022 + - https://github.com/advisories/GHSA-7fc5-f82f-cx69