North American Network Operators Group

Date Prev | Date Next | Date Index | Thread Index | Author Index | Historical

Re: Question about strain on the A root server

  • From: John Fraizer
  • Date: Sun Apr 23 02:10:24 2000

Update! Just talked to the client and he found the following code in:

/dev/.cas/

It was named binfo.c


I'm willing to bet that this may have something to do with the thrashing
on A as well as on various nameservers around the net.


int main (int argc, char *argv[])
{
if (argc < 2)
return 0;
checknamed(argv[1]);
}

/*
 * This code was written by:
 * Joshua James Drake ([email protected])
 *
 * 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 <sys/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>

/* added the versions i know to be vulnerable.  Turned it into a function for
 * my new scanner.  It doesn't return anything,  just a printf =)
 */

/* 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
checknamed(char *ip)
{
   struct sockaddr_in ra;

   if (!lookup_host(&ra, ip, 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;
     }
   herror("Unable to resolve hostname");
   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
   siginterrupt(SIGALRM, 1);
   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, temp1[256];
   int len;

   /* 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 */

  if(vul)
   {
   if(strstr(verstr, "8.2") && strstr(verstr, "-") == NULL && strstr(verstr, "-REL") == NULL)
    printf("%s: VULN: box running bind %s\n",host,verstr);
   }
  if(!vul)
   {
   if(strstr(verstr, "8.2")&& strstr(verstr, "-") == NULL && strstr(verstr, "-REL") == NULL)
    printf("%s: VULN: box running bind %s\n",host,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
}



---

John Fraizer
EnterZone, Inc