90s Vulns In 90s Software (Exim) - Is the Sky Falling?

90s Vulns In 90s Software (Exim) - Is the Sky Falling?

A few days ago, ZDI went public with no less than six 0days in the popular mail server Exim. Ranging from ‘potentially world-ending' through to ‘a bit of a damp squib’, these bugs were apparently discovered way back in June 2022 (!) - but naturally got caught up in the void between the ZDI and Exim for quite some time. Mysterious void.

As a brief background on Exim, “Exim is a message transfer agent (MTA) originally developed at the University of Cambridge for use on Unix systems connected to the Internet”. Exim is in use on millions of systems worldwide, and has a history of ‘interesting vulnerabilities’.

Given this, there has been a lot of panic about the issues (which we attempted to quell somewhat with our tweet thread on the issues), but they boil down to a few admittedly dangerous bugs that require a very specific environment to be accessible.

For example, CVE-2023-42117 is only going to affect you if you use Exim’s ‘proxy’ functionality with an untrusted proxy, which seems like an unlikely scenario. Here’s a quick rundown of the bugs and the functionality they depend on:

CVE CVSS Requirements
CVE-2023-42115 9.8 “External” authentication scheme configured and available
CVE-2023-42116 8.1 “SPA” module (used for NTLM auth) configured and available
CVE-2023-42117 8.1 Exim Proxy (different to a SOCKS or HTTP proxy) in use with untrusted proxy server
CVE-2023-42118 7.5 “SPF” condition used in an ACL
CVE-2023-42114 3.7 “SPA” module (used for NTLM auth) configured to authenticate the Exim server to an upstream server
CVE-2023-42119 3.1 An untrusted DNS resolver

You can see that the bugs have quite a lot of requirements. Most of us don’t need to worry. If you’re one of the unlucky ones who uses one of the listed features though, you’ll be keen to get more information before undertaking ZDI’s advice to “restrict interaction with the application”.

Fear not, watchTowr is here with some analysis! Let’s take a close look at that big scary CVSS 9.8 in the “External” authentication scheme, and see if it really is as scary as it sounds.

CVE-2023-42115

So, what is ‘external’ authentication all about, anyway?

Well, it enables authentication based on some properties which are ‘external’ to the SMTP session - usually an x509 certificate.

It is configured in the usual way in the Exim configuration file with a line such as driver = external, along with a handful of properties that directs the server to extract and test the correct information from the client. The Exim documentation gives an example similar to the following:

ext_ccert_san_mail:
  driver =            external
  public_name =       EXTERNAL

  server_param2 =     ${certextract {subj_altname,mail,>:} {$tls_in_peercert}}
  server_condition =  ${if forany {$auth2} {eq {$item}{$auth1}}}
  server_set_id =     $auth1

Slightly obtuse, but this is a mostly-readable method of verifying that the cert provided by the client has the correct ‘Subject Alternative Name’ for mail authentication, and then it proceeds to check that the username presented by the client (stored in the $auth1 variable) matches the certificate. This $auth1 is presented by the client in the form of a base64-encoded blob after the AUTH command.

This $auth1 variable is just one parameter that the external matcher takes, however. Other parameters can be provided, delimited by a binary NULL byte. These are put into variables $auth2 through $auth4. These are stored in the auth_vars global var.

uschar *auth_vars[AUTH_VARS];    // AUTH_VARS is 4 at this point

The code which parses these variables will carefully check that it does not write to this global beyond its 4-element limit, as we can see in a clarified version of get-data.c:

#define EXPAND_MAXN 20

int
auth_read_input(const uschar * data)
{
   if ((len = b64decode(data, &clear)) < 0)
     return BAD64;

   for (end = clear + len; clear < end && expand_nmax < EXPAND_MAXN; )
   {
      if (expand_nmax < AUTH_VARS)
         auth_vars[expand_nmax] = clear;
       expand_nstring[++expand_nmax] = clear;
       while (*clear != 0) 
          clear++;
       expand_nlength[expand_nmax] = clear++ - expand_nstring[expand_nmax];
   }
}

What happens if we supply more than four variables? Well, there’s no problem, there’s the crucial if (expand_nmax < AUTH_VARS) before writing to auth_vars. OOB write isn’t possible here.

However, the ‘external’ authenticator in particular misuses this functionality. It calls the code snippet above, which counts the variables, correctly ignoring any that are beyond AUTH_VARS. However, let’s see what the external authenticator does once auth_read_input has returned:

if (*data)
  if ((rc = auth_read_input(data)) != OK)
    return rc;

...

if (ob->server_param2)
{
  uschar * s = expand_string(ob->server_param2);
  auth_vars[expand_nmax] = s;       // 👀!!
  expand_nstring[++expand_nmax] = s;
  expand_nlength[expand_nmax] = Ustrlen(s);
  if (ob->server_param3)
  {
    s = expand_string(ob->server_param3);
    auth_vars[expand_nmax] = s;
    expand_nstring[++expand_nmax] = s;
    expand_nlength[expand_nmax] = Ustrlen(s);
  }
}

What’s that I spy? Is that an unguarded access to auth_vars, indexed by the expand_nmax that holds the number of variables observed, and may be anywhere up to EXPAND_MAXN (20)?!

I think so!

This enables an attacker to write two pointers, pointing to the data at ob->server_param2 and ob->server_param3, beyond the index of the auth_vars buffer.

Here’s an example SMTP session to show the overflow in action. Unfortunately, no segfault is produced, and the corruption is silent. We verified the overflow by adding extra logging to Exim.

EHLO watchtowr
> 250-host Hello root at watchtowr
AUTH external YWFhYQBhYWFhAGFhYWEAYWFhYQBhYWFhAGFhYWEAYWFhYQo=

Here, we’ve connected to the server, said hello (well, EHLO, which is an extended version of the usual HELO), and elected to perform external authentication via the AUTH external command, supplying a base64-encoded blob, as one would expect. The base64-encoded blob in question, however, contains seven fields, delimited by NULL bytes.

$ echo YWFhYQBhYWFhAGFhYWEAYWFhYQBhYWFhAGFhYWEAYWFhYQo= | base64 -d | hexdump.exe -C
00000000  61 61 61 61 00 61 61 61  61 00 61 61 61 61 00 61  |aaaa.aaaa.aaaa.a|
00000010  61 61 61 00 61 61 61 61  00 61 61 61 61 00 61 61  |aaa.aaaa.aaaa.aa|
00000020  61 61 0a                                          |aa.|

The first four variables are correctly parsed by auth_read_input, which duly sets expand_nmax to 7, since there are seven variables present. The ‘external’ authenticator then attempts to append the ob->server_param2 variable - resulting in the promised OOB write to auth_vars[7] .

Consequences

So, scary stuff! CVSS 9.8!

Not quite. Even if you do rely on this functionality, it is difficult to imagine how an attacker could craft a functional exploit given the constraints on the written data. Of course, it’s always possible - never say never! - but it seems unlikely to us.

So, our advice is the usual - patch when you can, once patches are available (Exim have stated they will release patches at 12:00 UTC today, Monday 2nd October). But in the meantime, don’t panic - this one is more of a damp squib than a world-ending catastrophe.

Brief FAQ

I bet you have loads of questions, so let’s have a simple FAQ to answer the burning questions you might have.

  • I don’t use SPA auth, or internal auth. I don’t have a malicious DNS server and I don’t use the spf condition in my ACLs. Finally, I don’t use that weird Exim Proxy thing. Do I have anything to worry about?

Not at all. Patch at your leisure if you like, but you’ve likely got zero exposure.

  • I don’t know if I use SPA or internal auth! How can I find out?!

Look in your Exim config (if you don’t know where it is, run exim4 -bP configure_file to find out). If you find the following lines, you are likely using one of the affected authentication mechanisms:

driver = external

Or for SPA:

driver = spa
  • Why are we only seeing disclosure now, given that the initial report was in June 2022?

🤷

Please exercise caution and apply patches to systems you identify internally as running an affected version of Exim - but, luckily(?) this is not the end-of-the-world moment it was purported to be.

If you'd like to learn more about how the watchTowr Platform, our Attack Surface Management and Continuous Automated Red Teaming solution, please get in touch.