[28270] in North American Network Operators' Group
Re: Question about strain on the A root server
daemon@ATHENA.MIT.EDU (John Fraizer)
Sun Apr 23 02:09:22 2000
Date: Sun, 23 Apr 2000 02:07:03 -0400 (EDT)
From: John Fraizer <nanog@EnterZone.Net>
To: jlewis@lewis.org
Cc: Dirk Harms-Merbitz <dirk@power.net>, nanog@merit.edu
In-Reply-To: <Pine.LNX.4.10.10004230133160.25904-100000@redhat1.mmaero.com>
Message-ID: <Pine.LNX.3.96.1000423020256.10537A-100000@Overkill.EnterZone.Net>
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Errors-To: owner-nanog-outgoing@merit.edu
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 (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 <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