aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2020-08-03 20:27:38 +0200
committerGuilhem Moulin <guilhem@fripost.org>2020-08-03 20:50:08 +0200
commit3b2939febdeb7f92051f95a3b08cf86e221ce21d (patch)
tree5af420e5db686b913e2f5126b5d026e5d79e3fa3
parentbc43c0d9468a8d50ba141c8a965f9f07ed0456ff (diff)
libinterimap: abort on PREAUTH greeting received on plaintext connections
Set "STARTTLS = NO" to ignore. This is similar to CVE-2020-12398 and CVE-2020-14093.
-rw-r--r--Changelog3
-rw-r--r--lib/Net/IMAP/InterIMAP.pm11
-rw-r--r--tests/list1
-rwxr-xr-xtests/preauth-plaintext/imapd44
l---------tests/preauth-plaintext/interimap.remote1
-rw-r--r--tests/preauth-plaintext/t19
6 files changed, 78 insertions, 1 deletions
diff --git a/Changelog b/Changelog
index c6194de..1327c00 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,9 @@ interimap (0.5.2) UNRELEASED;
and \[rq] in the groff output anyway).
- libinterimap: fix response injection vulnerability after STARTTLS.
For background see https://gitlab.com/muttmua/mutt/-/issues/248 .
+ - libinterimap: abort on PREAUTH greeting received on plaintext
+ connections (set "STARTTLS = NO" to ignore). This is similar to
+ CVE-2020-12398 and CVE-2020-14093.
* libinterimap: fail when a capability to ENABLE is missing from the
server's CAPABILITY listing.
diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm
index f0dd2df..b01e1a9 100644
--- a/lib/Net/IMAP/InterIMAP.pm
+++ b/lib/Net/IMAP/InterIMAP.pm
@@ -464,6 +464,7 @@ sub new($%) {
$self->logger('S: xxx ', $IMAP_text);
$self->{debug} = $dbg;
}
+ $self->{_STATE} = 'AUTH';
unless ($IMAP_text =~ /\A\Q$IMAP_cond\E \[CAPABILITY /) {
# refresh the CAPABILITY list since the previous one had only pre-login capabilities
@@ -471,7 +472,15 @@ sub new($%) {
$self->capabilities();
}
}
- $self->{_STATE} = 'AUTH';
+ elsif ($IMAP_cond eq 'PREAUTH') {
+ if ($self->{type} eq 'imap' and $self->{STARTTLS} != 0) {
+ $self->fail("PREAUTH greeting on plaintext connection? MiTM in action? Aborting, set \"STARTTLS = NO\" to ignore.");
+ }
+ $self->{_STATE} = 'AUTH';
+ }
+ else {
+ $self->panic();
+ }
# Don't send the COMPRESS command before STARTTLS or AUTH, as per RFC 4978
if ($self->{compress} // 1 and
diff --git a/tests/list b/tests/list
index 5522ba8..db77f50 100644
--- a/tests/list
+++ b/tests/list
@@ -38,6 +38,7 @@ repair --repair
auth-login LOGIN
auth-logindisabled LOGINDISABLED
auth-noplaintext abort when STARTTLS is not offered
+ preauth-plaintext abort on MiTM via PREAUTH greeting
compress COMPRESS=DEFLATE
condstore CONDSTORE
diff --git a/tests/preauth-plaintext/imapd b/tests/preauth-plaintext/imapd
new file mode 100755
index 0000000..8f3ac30
--- /dev/null
+++ b/tests/preauth-plaintext/imapd
@@ -0,0 +1,44 @@
+#!/usr/bin/perl -T
+
+use warnings;
+use strict;
+
+use Errno qw/EINTR/;
+use Socket qw/INADDR_LOOPBACK AF_INET SOCK_STREAM pack_sockaddr_in
+ SOL_SOCKET SO_REUSEADDR SHUT_RDWR/;
+
+socket(my $S, AF_INET, SOCK_STREAM, 0) or die;
+setsockopt($S, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die;
+bind($S, pack_sockaddr_in(10143, INADDR_LOOPBACK)) or die "bind: $!\n";
+listen($S, 1) or die "listen: $!";
+
+while (1) {
+ my $sockaddr = accept(my $conn, $S) or do {
+ next if $! == EINTR;
+ die "accept: $!";
+ };
+
+ # minimum CAPABILITY list, see tests/snippets/dovecot/interimap-required-capabilities.conf
+ $conn->printflush("* PREAUTH [CAPABILITY IMAP4rev1 ENABLE UIDPLUS LIST-EXTENDED QRESYNC LIST-STATUS] IMAP4rev1 Server\r\n");
+ my $x;
+
+ $x = $conn->getline() // next;
+ $x =~ /\A(\S+) ENABLE QRESYNC\r\n/ or die;
+ $conn->printflush("* ENABLED QRESYNC\r\n$1 OK ENABLE completed\r\n");
+
+ $x = $conn->getline() // next;
+ $x =~ /\A(\S+) LIST .*\r\n/ or die;
+ $conn->print("* LIST (\\Noselect) \"~\" \"\"\r\n");
+ $conn->print("* LIST () \"~\" INBOX\r\n");
+ $conn->print("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1 HIGHESTMODSEQ 1)\r\n");
+ $conn->printflush("$1 OK LIST completed\r\n");
+
+ close($conn);
+}
+
+END {
+ if (defined $S) {
+ shutdown($S, SHUT_RDWR) or warn "shutdown: $!";
+ close($S) or print STDERR "Can't close: $!\n";
+ }
+}
diff --git a/tests/preauth-plaintext/interimap.remote b/tests/preauth-plaintext/interimap.remote
new file mode 120000
index 0000000..ad49677
--- /dev/null
+++ b/tests/preauth-plaintext/interimap.remote
@@ -0,0 +1 @@
+../starttls/interimap.remote \ No newline at end of file
diff --git a/tests/preauth-plaintext/t b/tests/preauth-plaintext/t
new file mode 100644
index 0000000..427d57b
--- /dev/null
+++ b/tests/preauth-plaintext/t
@@ -0,0 +1,19 @@
+# Test IMAP MiTM via PREAUTH greeting
+# For background see CVE-2020-12398, CVE-2020-14093 and
+# https://gitlab.com/muttmua/mutt/commit/3e88866dc60b5fa6aaba6fd7c1710c12c1c3cd01
+
+env -i USER="remote" HOME="$HOME_remote" "$TESTDIR/imapd" & PID=$!
+trap "ptree_abort $PID" EXIT INT TERM
+
+! interimap --debug || error
+grep -Fx 'remote: ERROR: PREAUTH greeting on plaintext connection? MiTM in action? Aborting, set "STARTTLS = NO" to ignore.' <"$STDERR" || error
+! grep '^remote: C: ' <"$STDERR" || error "wrote command in MiTM'ed PREAUTH connection!"
+
+
+# Ignore the warning when STARTTLS is explicitely disabled
+echo "STARTTLS = NO" >>"$XDG_CONFIG_HOME/interimap/config"
+interimap --debug || true
+
+grep -Fx "remote: S: * STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1 HIGHESTMODSEQ 1)" <"$STDERR" || error
+
+# vim: set filetype=sh :