more named software

From: Joshua J. Drake (jdrakeat_private)
Date: Mon Jun 08 1998 - 21:21:34 PDT

  • Next message: Lloyd Vancil: "Scanning Attacks from apple.com are spoofed addresses"

    This may or may not be a large problem depending on what you consider a breach
    of security.  The attached program allows someone to view your name server's
    inverse query
    support status and the version of named that you run from remote.  As mentioned
    in the "named warez" post, you can use dig to get the version information.  IMHO
    certain information should not be given out to anyone that asks for it.  When a
    system cracker scans your machine for faulty software, they will most likely
    be looking for version numbers of the software you are running. Almost all
    exploits are version dependent.  For example, sendmail, httpd, named, and not to
    mention some local exploits too.
    
    So to sum it up, giving out version numbers and similar information aids a
    cracker when attacking a system.  This will by no means prevent someone from
    "owning" you, but it may discourage inexperienced "script kiddies" from trying
    some attacks.
    
    Joshua James Drake
    jdrakeat_private
    http://www.pulsar.net/~jdrake/
    
    Attached file: binfo-udp.c
    --- code begins here ---
    
    /*
     * This code was written by:
     * Joshua James Drake (jdrakeat_private)
     *
     * 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>
    
    /* 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;
         }
       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;
       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
    }
    
    --- code ends here ---
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:57:01 PDT