[exim-dev] Hotfix for some of the ZDI vulnerabilities

Top Page
Delete this message
Reply to this message
Author: Florian Zumbiehl
Date:  
To: exim-dev
Subject: [exim-dev] Hotfix for some of the ZDI vulnerabilities
Hi,

below you find a patch that fixes some (probably three?) of what I guess are
the vulnerabilities reported by ZDI.

Please note that the patch is only mildly tested, it is developed based on
the git master branch, but can be applied to older versions with minor
massaging. If you go back far enough, proxy.c was part of smtp_in.c, but if
you adjust for that, the patch can be made to apply there, too.

Obviously, I have no idea whether this actually addresses what ZDI has
reported, but if not, these probably should be fixed, too, and if so, given
the fact that I managed to rather easily find these vulnerabilities based
on the information that's publicly available, I don't think there is much
point to trying to keep this secret any longer--if anything, it's
counterproductive.

Also mind you that this is a hot fix, it's neither elegant, nor does it do
any useful error reporting, the goal was simply to prevent out of bounds
accesses.

Florian

---

diff --git a/src/src/auths/external.c b/src/src/auths/external.c
index 078aad0..54966e6 100644
--- a/src/src/auths/external.c
+++ b/src/src/auths/external.c
@@ -101,6 +101,9 @@ if (expand_nmax == 0)     /* skip if rxd data */
   if ((rc = auth_prompt(CUS"")) != OK)
     return rc;


+if (expand_nmax != 1)
+ return FAIL;
+
if (ob->server_param2)
{
uschar * s = expand_string(ob->server_param2);
diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c
index 222ccea..66967d6 100644
--- a/src/src/auths/spa.c
+++ b/src/src/auths/spa.c
@@ -166,12 +166,18 @@ if (auth_get_no64_data(&data, msgbuf) != OK)
return FAIL;

 /* dump client response */
-if (spa_base64_to_bits(CS &response, sizeof(response), CCS data) < 0)
+int l = spa_base64_to_bits(CS &response, sizeof(response), CCS data);
+if (l < 0)
   {
   DEBUG(D_auth) debug_printf("auth_spa_server(): bad base64 data in "
     "response: %s\n", data);
   return FAIL;
   }
+if(l < (char *)&response.buffer - (char *)&response)return FAIL;
+unsigned long o = IVAL(&response.uUser.offset, 0);
+if((l < o) || (l - o < SVAL(&response.uUser.len, 0)))return FAIL;
+o = IVAL(&response.ntResponse.offset, 0);
+if((l < o) || (l - o < 24))return FAIL;


/***************************************************************
PH 07-Aug-2003: The original code here was this:
@@ -346,7 +352,10 @@ if (!smtp_read_response(sx, US buffer, buffsize, '3', timeout))

/* convert the challenge into the challenge struct */
DSPA("\n\n%s authenticator: challenge (%s)\n\n", ablock->name, buffer + 4);
-spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));
+int l = spa_base64_to_bits(CS (&challenge), sizeof(challenge), CCS (buffer + 4));
+if((l < 0) || (l < (char *)&challenge.buffer - (char *)&challenge))return FAIL;
+unsigned long o = IVAL(&challenge.uDomain.offset, 0);
+if((l < o) || (l - o < SVAL(&challenge.uDomain.len, 0)))return FAIL;

 spa_build_auth_response(&challenge, &response, CS username, CS password);
 spa_bits_to_base64(US msgbuf, US &response, spa_request_length(&response));
diff --git a/src/src/proxy.c b/src/src/proxy.c
index fbce111..8dd7034 100644
--- a/src/src/proxy.c
+++ b/src/src/proxy.c
@@ -93,6 +93,8 @@ while (capacity > 0)
   do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
   if (ret == -1)
     return -1;
+  if (!ret)
+    break;
   have++;
   if (last)
     return have;
@@ -254,6 +256,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
     goto proxyfail;
     }


+  if (ret < 16)
+    goto proxyfail;
   /* The v2 header will always be 16 bytes per the spec. */
   size = 16 + ntohs(hdr.v2.len);
   DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n",
@@ -274,7 +278,7 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
       {
       retmore = read(fd, (uschar*)&hdr + ret, size-ret);
       } while (retmore == -1 && errno == EINTR && !had_command_timeout);
-    if (retmore == -1)
+    if (retmore < 1)
       goto proxyfail;
     DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore);
     ret += retmore;
@@ -297,6 +301,8 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
       switch (hdr.v2.fam)
         {
         case 0x11:  /* TCPv4 address type */
+      if (ret < 28)
+            goto proxyfail;
           iptype = US"IPv4";
           tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
           inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
@@ -323,6 +329,8 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
           proxy_external_port  = tmpport;
           goto done;
         case 0x21:  /* TCPv6 address type */
+      if (ret < 52)
+            goto proxyfail;
           iptype = US"IPv6";
           memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
           inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
@@ -381,10 +389,13 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0)
     goto proxyfail;
   ret += r2;


+  if(ret > 107)
+    goto proxyfail;
+  hdr.v1.line[ret] = 0;
   p = string_copy(hdr.v1.line);
   end = memchr(p, '\r', ret - 1);


-  if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n')
+  if (!end || end[1] != '\n')
     {
     DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
     goto proxyfail;


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-dev.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-dev-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/