middleman-1.2 and prior off-by-one bug

From: qitest1 (qitest1at_private)
Date: Fri Jan 10 2003 - 13:18:15 PST

  • Next message: Martin Schulze: "[SECURITY] [DSA 227-1] New openldap packages fix buffer overflows and remote exploit"

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1
    
    
    	QITEST1 SECURITY ADVISORY #006
    
    middleman-1.2 and prior off-by-one bug
    
    PROGRAM DESCRIPTION
    Middleman is a powerful proxy server with many features designed to make browsing
    the Internet a more pleasant experience. It can do much more than just proxying
    though; it can be used as a layer between any web server and client to filter HTTP
    requests, or act as a portal between an internal network and the Internet. It has
    an intuitive Web interface that provides an easy way of accessing and changing the
    proxy's configuration, there's no need to dig through any complicated configuration
    files.
    (quoted from its README.html)
    
    VULNERABILITY DESCRIPTION
    The program is affected by an ebp corruption condition in the routine performing the
    dns lookup of the hostname provided in the request, because its own implementation of
    strncpy goes off by one.
    The bug could be easily exploited by a remote attacker, leading to a root shell if
    the daemon runs as root (it's really nice that the sample rc init file provided
    calls the daemon without dropping privileges, which is one of its features).
    If exploitation is not successful, it will result in a denial of service, because the
    program will die at all.
    
    DETAILS
    In src/misc.c:
    /*
    strncpy which always NULL terminates
    */
    char *s_strncpy(char *d, char *s, size_t len)
    {
            char *dest = d;
    
            for (; len && (*dest = *s); s++, dest++, len--);
            *dest = '\0';
    
            return d;
    }
    
    No doubt. It always NULL terminates. But even off by one.
    
    In src/networks.c near line 614 we find:
    /*
    perform a dns lookup, using cached response from a previous lookup if possible
    */
    HOSTENT *net_dns(char *host)
    {
            time_t t;
            char *string, hst[128], buf[24];
            HOSTENT *hostent;
    
    Looking to this code we could think the memory layout on the stack for this function
    is the following:
    hst[128]		|
    string[4]		|
    t[4]			|
    ebp			|
    eip			V
    
    In src/networks.c near line 627 we find:
            s_strncpy(hst, host, 128);
    
    We know that s_strncpy puts its final 0x00 off by one. This fact, apparently, should
    not be a real problem, because the 0x00 byte should be put on the lsb of string.
    
    Where is the bug?
    
    Due to compilation (maybe optimization), those variables get allocated in a
    different way:
    (I dumped this from the program)
    ** net_dns(): &hst 0xbf7ff9f4 &string 0xbf7ff9d4 &t 0xbf7ff9d8
    
    So the real memory layout is:
    string[4]		|
    t[4]			|
    hst[128]		|
    ebp                     |
    eip                     V
    
    So we have ebp corruption.
    Let's have a memory dump:
    ** net_dns(): 0xbf7ff9f4 -> 0xbfffc0d3 (0)
    ** net_dns(): 0xbf7ff9f8 -> 0xbfffc0d3 (1)
    ** net_dns(): 0xbf7ff9fc -> 0xbfffc0d3 (2)
    [...]
    ** net_dns(): 0xbf7ffa70 -> 0xbfffc0d3 (31)
    ** net_dns(): 0xbf7ffa74 -> 0xbf7ffa00 (32)
    ** net_dns(): 0xbf7ffa78 -> 0x804db07 (33)
    
    At 0xbf7ffa78 we have 0x804db07: that's the eip. Indeed:
    (gdb) x 0x804db07
    0x804db07 <net_connect+75>:     0xc483c289
    net_connect() effectively calls net_dns()
    
    At 0xbf7ffa74 there is the corrupted ebp.
    At 0xbf7ffa00 + 4 the execution flow will search an eip:
    ** net_dns(): 0xbf7ffa04 -> 0xbfffc0d3 (4)
    We can control the data contained at that address, which can obviously be a pointer to
    our code. But there's no need to explain here how this kind of vulnerability can be
    exploited: it's quite trivial.
    
    I think this kind of problems should be seriously valued, because they are
    difficult to be detected and prevented. But they can be avoided by simply checking
    that the program never goes off by one, even if the buffer is not immediately
    before the ebp: indeed there could be a condition like that I described.
    
    CREDITS
    h2so4 - support and connectivity
    
    SOLUTION
    Apply the following patch.
    
    8<
    - --- middleman/src/misc.c	2002-10-19 19:07:24.000000000 +0200
    +++ middleman-patched/src/misc.c	2003-01-10 11:29:08.000000000 +0100
    @@ -27,17 +27,34 @@
     #include <sys/types.h>
     #include "proto.h"
    
    - -/*
    - -strncpy which always NULL terminates
    - -*/
    - -char *s_strncpy(char *d, char *s, size_t len)
    - -{
    - -	char *dest = d;
    - -
    - -	for (; len && (*dest = *s); s++, dest++, len--);
    - -	*dest = '\0';
    +	/* Adapted version of OpenBSD strlcpy */
    +char *
    +s_strncpy(dst, src, siz)
    +	char *dst;
    +	char *src;
    +	size_t siz;
    +{
    +	register char *d = dst;
    +	register const char *s = src;
    +	register size_t n = siz;
    +
    +	/* Copy as many bytes as will fit */
    +	if (n != 0 && --n != 0) {
    +		do {
    +			if ((*d++ = *s++) == 0)
    +				break;
    +		} while (--n != 0);
    +	}
    +
    +	/* Not enough room in dst, add NUL and traverse rest of src */
    +	if (n == 0) {
    +		if (siz != 0)
    +			*d = '\0';		/* NUL-terminate dst */
    +		while (*s++)
    +			;
    +	}
    
    - -	return d;
    +	return dst;
     }
    
     /*
    8<
    
      --    -------------------------------------------
      ---- q1--    ----------------------------------------
      --    -------------------------------------------
      Web: http://bespin.org/~qitest1
      GPG public key: http://bespin.org/~qitest1/qitest1.gpg.key
      - --------------------------------------------------------
    
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.0.6 (GNU/Linux)
    Comment: For info see http://www.gnupg.org
    
    iD8DBQE+Hq4YIrsshIyVmPkRAiX+AKCjJbe2lK4wIEkSDwk08eKbRqzVEgCfYOrx
    wIX35R8SlitiQ82VLe9itVs=
    =qTBd
    -----END PGP SIGNATURE-----
    



    This archive was generated by hypermail 2b30 : Wed Jan 15 2003 - 17:18:09 PST