From 82e2db5a2314a31bfd6c3f33123d9cf3f08dd301 Mon Sep 17 00:00:00 2001 From: tjuhasz Date: Thu, 18 Jun 2026 15:32:58 +0200 Subject: [PATCH] Fix for CVE-2026-42246. Fix Net::IMAP STARTTLS stripping vulnerability. Advisory: https://github.com/advisories/GHSA-vcgp-9326-pqcp Sourced from: - https://github.com/ruby/net-imap/commit/62eea6ff - https://github.com/ruby/net-imap/commit/24d5c773 Resolves: RHEL-181753 --- ruby4.0.spec | 10 ++ ...ripping-vulnerability-CVE-2026-42246.patch | 125 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 rubygem-net-imap-0.6.2-STARTTLS-stripping-vulnerability-CVE-2026-42246.patch diff --git a/ruby4.0.spec b/ruby4.0.spec index 9c177d8..dd02a5a 100644 --- a/ruby4.0.spec +++ b/ruby4.0.spec @@ -349,6 +349,13 @@ Patch12: ruby-4.0.3-Fix-a-format-string-injection-vulnerability.patch # - https://github.com/ruby/net-imap/commit/49c516d6 # - https://github.com/ruby/net-imap/commit/6f82e28f Patch13: rubygem-net-imap-0.6.2-ResponseReader-quadratic-complexity-CVE-2026-42245.patch +# CVE-2026-42246 +# Fix Net::IMAP STARTTLS stripping vulnerability +# Advisory: https://github.com/advisories/GHSA-vcgp-9326-pqcp +# Sourced from: +# - https://github.com/ruby/net-imap/commit/62eea6ff +# - https://github.com/ruby/net-imap/commit/24d5c773 +Patch14: rubygem-net-imap-0.6.2-STARTTLS-stripping-vulnerability-CVE-2026-42246.patch %{?with_rubypick:Suggests: rubypick} @@ -516,6 +523,7 @@ popd pushd .bundle/gems/net-imap-%{net_imap_version} %patch 13 -p1 +%patch 14 -p1 popd # Provide an example of usage of the tapset: @@ -1662,6 +1670,8 @@ make -C %{_vpath_builddir} runruby TESTRUN_SCRIPT=" \ - Fix Net::IMAP ResponseReader quadratic complexity vulnerability (CVE-2026-42245) Includes core fix plus additional performance optimizations Resolves: RHEL-181680 +- Fix Net::IMAP STARTTLS stripping vulnerability (CVE-2026-42246) + Resolves: RHEL-181753 * Wed Apr 29 2026 Tomas Juhasz - 4.0.3-34 - Upgrade to Ruby 4.0.3. diff --git a/rubygem-net-imap-0.6.2-STARTTLS-stripping-vulnerability-CVE-2026-42246.patch b/rubygem-net-imap-0.6.2-STARTTLS-stripping-vulnerability-CVE-2026-42246.patch new file mode 100644 index 0000000..35941ba --- /dev/null +++ b/rubygem-net-imap-0.6.2-STARTTLS-stripping-vulnerability-CVE-2026-42246.patch @@ -0,0 +1,125 @@ +From 62eea6ffe1e390060065169474f97edbc42bd2b2 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Fri, 27 Mar 2026 17:31:11 -0400 +Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=92=F0=9F=A5=85=20Ensure=20STARTTL?= + =?UTF-8?q?S=20tagged=20response=20was=20handled?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Taking a "belt-and-suspenders" approach to a STARTTLS stripping attack: + +This handles `STARTTLS` as a special-case: if the `STARTTLS` handler +did not run, for _whatever_ reason, an exception _must_ be raised and +the connection dropped. + +_No_ command should ever receive a tagged `OK` prior to completely +sending the command. But `STARTTLS` is security-sensitive enough to +warrant this special-case handler. +--- + lib/net/imap.rb | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/lib/net/imap.rb b/lib/net/imap.rb +index 00f1588..e2cbc5a 100644 +--- a/lib/net/imap.rb ++++ b/lib/net/imap.rb +@@ -1390,9 +1390,11 @@ module Net + # + def starttls(**options) + @ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options) ++ handled = false + error = nil + ok = send_command("STARTTLS") do |resp| + if resp.kind_of?(TaggedResponse) && resp.name == "OK" ++ handled = true + clear_cached_capabilities + clear_responses + start_tls_session +@@ -1404,6 +1406,13 @@ module Net + disconnect + raise error + end ++ unless handled ++ disconnect ++ raise InvalidResponseError, ++ "STARTTLS handler was bypassed, although server responded %p" % [ ++ ok.raw_data.chomp ++ ] ++ end + ok + end + + + +From 24d5c773d1bb76ca1cd0a26b2218195011c16969 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Fri, 27 Mar 2026 18:00:09 -0400 +Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=92=F0=9F=A5=85=20Handle=20tagged?= + =?UTF-8?q?=20"OK"=20to=20incomplete=20command?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Taking a "belt-and-suspenders" approach: + +This is a potential problem for any command which registers a response +handler: a malicious server can easily guess what the next tag will be, +and send an `OK` response _before_ the client the response handler is +attached. + +`STARTTLS` is an extreme example of this issue: if the `STARTTLS` +handler does not run, then `#starttls` will not start the TLS session, +and the connection is not secured, _but no error is raised._ + +We should _also_ attach the response handler before sending the `CRLF`, +but that is neither necessary (the response handler will added before +the `synchronize` mutex is unlocked) nor sufficient (the fake `OK` can +be sent _much_ earlier). + +On the other hand, it _is_ okay for the server to send an error tagged +response (`NO` or `BAD`), before sending the command has completed. +--- + lib/net/imap.rb | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/lib/net/imap.rb b/lib/net/imap.rb +index e2cbc5a..6af1ca5 100644 +--- a/lib/net/imap.rb ++++ b/lib/net/imap.rb +@@ -3086,6 +3086,7 @@ module Net + + synchronize do + tag = Thread.current[:net_imap_tag] = generate_tag ++ guard_against_tagged_response_skipping_handler!(tag, "IDLE") + put_string("#{tag} IDLE#{CRLF}") + + begin +@@ -3550,6 +3551,7 @@ module Net + put_string(" ") + send_data(i, tag) + end ++ guard_against_tagged_response_skipping_handler!(tag, cmd) + put_string(CRLF) + if cmd == "LOGOUT" + @logout_command_tag = tag +@@ -3565,6 +3567,19 @@ module Net + end + end + end ++ rescue InvalidResponseError ++ disconnect ++ raise ++ end ++ ++ def guard_against_tagged_response_skipping_handler!(tag, cmd) ++ return unless (resp = @tagged_responses[tag])&.name&.upcase == "OK" ++ raise InvalidResponseError, format( ++ "Received tagged 'OK' to incomplete %s command (tag=%s). " \ ++ "This could indicate a malicious server, a man-in-the-middle, or " \ ++ "client-side command injection. Disconnecting.", ++ cmd, tag ++ ) + end + + def generate_tag