oss-sec mailing list archives

Exim 4.96 overflow


From: Evgeny Legerov <admin () vulndisco cc>
Date: Tue, 9 Aug 2022 15:03:34 +0300

Hi,


Yet another interesting issue in Exim 4.96, it is OpenBSD specific.

Combination OpenBSD + Exim is very rare, so it probably affects only two boxes in the world,one of them is my vm.


OpenBSD dn_expand() source:
int
dn_expand(const u_char *msg, const u_char *eomorig, const u_char *comp_dn,
    char *exp_dn, int length)
{
const u_char *cp;
        char *dn;
        int n, c;
        char *eom;
        int len = -1, checked = 0;

        dn = exp_dn;
        cp = comp_dn;
        if (length > HOST_NAME_MAX)
                length = HOST_NAME_MAX;
        eom = exp_dn + length;
        while ((n = *cp++)) {
                switch (n & INDIR_MASK) {
                case 0:
                        if (dn != exp_dn) {
                                if (dn >= eom)
                                        return (-1);
                                *dn++ = '.';
                        }
                        if (dn+n >= eom)
                                return (-1);
                        checked += n + 1;
                        while (--n >= 0) {
                                if (((c = *cp++) == '.') || (c == '\\')) {
                                        if (dn + n + 2 >= eom)
                                                return (-1);
                                        *dn++ = '\\';
                                }
                                *dn++ = c;
                                if (cp >= eomorig)      /* out of range */
                                        return (-1);
                        }
                        break;

                case INDIR_MASK:
                        if (len < 0)
                                len = cp - comp_dn + 1;
                        cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
                        if (cp < msg || cp >= eomorig)  /* out of range */
                                return (-1);
                        checked += 2;
                        /*
                         * Check for loops in the compressed name;
                         * if we've looked at the whole message,
                         * there must be a loop.
                         */
                        if (checked >= eomorig - msg)
                                return (-1);
                        break;

                default:
                        return (-1);                    /* flag error */
                }
        }
        *dn = '\0';
        if (len < 0)
                len = cp - comp_dn;
        return (len);
}

As we can see, dn_expand() does not escape special characters, in particular it ignores '\n'. In case of Exim, after it does a reverse dns lookup, the answer is parsed using dn_expand() and

it is stored in 'sender_host_name' global variable.
This variable is written into spool header file.


Many interesting things can happen when we control the contents of spool file:

 if (flags & 0x01)      /* one_time data exists */
      {
      int len;
      while (isdigit(*(--p)) || *p == ',' || *p == '-');
      (void)sscanf(CS p+1, "%d,%d", &len, &pno);
      *p = 0;
      if (len > 0)
        {
        p -= len;
[1]        errors_to = string_copy_taint(p, GET_TAINTED);
        }
      }

[2]    *--p = 0;   /* Terminate address */

As long as we control 'len' variable, we have out of bounds read on line #1, and out of bounds write on line #2.

It may not be very practical attack,as someone says you need arp spoofing for this attack to work.

Your opinions would be very interesting.


regards,

-e



Current thread: