/*
 * 
 * bind_bof
 *
 * This plugin was written by Renaud Deraison and is released
 * under the GPL
 *
 */
/*
   tests if vulnerable to overflow for bind (pre 8.1.2 / 4.9.8) named
   nameserver must have fake-iquery option turned on
   written solely by Joshua J. Drake (jdrake@pulsar.net)
*/

#include <includes.h>
#include <arpa/nameser.h>


void handle_alarm();
int lookup_host();
int attack_bind();
int make_keypkt();
int send_packet();
int get_packet();
int vulnerable();

#define NAME "BIND buffer overrun"
#define DESC "BIND 4.9 releases prior to BIND 4.9.7 and BIND 8 releases prior\n\
to 8.1.2 do not properly bounds check a memory copy when responding to an inverse\n\
query request. An improperly or maliciously formatted inverse query on a\n\
TCP stream can crash the server or allow an attacker to gain root \n\n\
privileges. This plugin determines if your BIND daemon can be affected by such \n\
an attack without actually gaining root access.\n\n\
Risk factor : high"

#define COPYRIGHT "Original code by Joshua J. Drake (jdrake@pulsar.net)"
#define SUMM "determines if BIND can be attacked by a buffer overflow"    


PlugExport int plugin_init(struct arglist * desc);
PlugExport int plugin_init(struct arglist * desc)
{
 plug_set_name(desc, NAME);
 plug_set_description(desc, DESC);
 plug_set_summary(desc, SUMM);
 plug_set_copyright(desc, COPYRIGHT);
 plug_set_category(desc, ACT_ATTACK);
 plug_set_family(desc, "Gain root remotely");
 return(0);                     
}

PlugExport int plugin_run(struct arglist * env);
PlugExport int plugin_run(struct arglist * env)
{
   if(!host_get_port_state(env, 53))return(0);
   srand(time(NULL));
   if(attack_bind(env))
    post_hole(env, 53, 
"The remote BIND do not properly bounds check a memory copy when responding \n\
to an inverse query request. An improperly or maliciously formatted inverse \n\
query on a TCP stream can crash the server or allow an attacker to gain root\n\
privileges.\n\
Solution : upgrade");
   return(0);
}


int
attack_bind(env)
   struct arglist * env;
{
   int sd, pktlen;
   char keypkt[512], inbuf[512];
   sd = open_sock_tcp(env, 53);
   pktlen = make_keypkt(keypkt);
   if (!send_packet(sd, keypkt, pktlen))
      return(0);
   if (!get_packet(sd, inbuf, &pktlen) || pktlen <= 0)
      return(0);
      
   socket_close(sd);
   if (vulnerable(inbuf))return(1);
   else return(0);
   
}



int
make_keypkt(pktbuf)
   char *pktbuf;
{
   HEADER *dnsh;
   char *ptr = pktbuf;
   int pktlen = 0;
   unsigned long ttl = 31337;
   unsigned long addr = inet_addr("1.2.3.4");

   memset(pktbuf, 0, sizeof(pktbuf));

/* fill the dns header */
   dnsh = (HEADER *)ptr;
   dnsh->id		= htons(rand()%65535);
   dnsh->qr		= 0;
   dnsh->opcode		= IQUERY;
   dnsh->aa		= 0;
   dnsh->tc		= 0;
   dnsh->rd		= 1;
   dnsh->ra		= 1;
   dnsh->unused		= 0;
/* removed for portability (it's zero already)
   dnsh->pr		= 0;
 */
   dnsh->rcode		= 0;
   dnsh->qdcount	= htons(0);
   dnsh->ancount	= htons(1);
   dnsh->nscount	= htons(0);
   dnsh->arcount	= htons(0);
   pktlen += sizeof(HEADER);
   ptr += sizeof(HEADER);
/* this is the domain name (nothing here) */
   *(ptr++) = '\0';
   pktlen++;
/* fill out the rest of the rr */
   PUTSHORT(T_A, ptr);
   PUTSHORT(C_IN, ptr);
   PUTLONG(ttl, ptr);
   PUTSHORT(4, ptr);
   PUTLONG(addr, ptr);
   ptr += 4;
   pktlen += ((sizeof(short) * 3) + sizeof(long) + 4);

   return pktlen;
}

int
send_packet(sd, pktbuf, pktlen)
   int sd, pktlen;
   char *pktbuf;
{
   char tmp[2], *tmpptr;

   tmpptr = tmp;
   PUTSHORT(pktlen, tmpptr);
   if (write(sd, tmp, 2) != 2 || write(sd, pktbuf, pktlen) != pktlen)
     {
	return 0;
     }
   return 1;
}

int
get_packet(sd, pktbuf, pktlen)
   int sd, *pktlen;
   char *pktbuf;
{
   char tmp[2], *tmpptr;

   tmpptr = tmp;
   if (read(sd, tmp, 2) != 2)
     {
	return 0;
     }
   GETSHORT(*pktlen, tmpptr);
   if (read(sd, pktbuf, *pktlen) != *pktlen)
     {
	return 0;
     }
   return 1;
}

int
vulnerable(pktbuf)
   char *pktbuf;
{
   HEADER *dnsh = (HEADER *)pktbuf;

   if (dnsh->rcode == 0)
      return 1;
   return 0;
}
