Exploit-DB #22014
Description: Traceroute-nanog 6 - Local Buffer Overflow
Type: local
Platform: linux
Date: 2002-11-12
Author: Carl Livitt

Exploit:
// source: https://www.securityfocus.com/bid/6166/info

A vulnerability has been discovered in Traceroute-nanog. It has been reported that Traceroute-nanog contains a buffer overflow condition.

The overflow occurs in the 'get_origin()' function in the 'traceroute.c' file. Due to insufficient bounds checking performed by the whois parser, it may be possible to cause 'get_origin()' to corrupt memory on the system stack.

This vulnerability can be exploited by an attacker to gain root privileges on a target host. 

/*

---[ Traceroute-nanog 6.0 -> 6.1.1 exploit ]---

By Carl Livitt (carl@learningshophull.co.uk)

Exploits a stack overflow in get_origin() function of traceroute.c to gain r00t.
Tested on SuSE 7.1, 7.2, 7.3 & 8.0, but should work on 7.0 and 6.x.

There are lots more overflows in this traceroute implementation... mostly heap
overflows I think. Have a look, have some fun.


---[ About this exploit ]---

Traceroute-nanog can do WHOIS-like DNS lookups at each hop and find the admin email address
for each IP. It is possible to set environment variables to tell traceroute
the IP and port number of your own custom DNS server.

Unfortunately, traceroute fails to error-check the returned records, making it possible
to trick it into causing a stack overflow (but with limitations).

My technique was to write my own malicious server that would inject a carefully
crafted response to traceroute's query, triggering the overflow and letting
me obtain local r00t access.

---[ More Info ]---

When get_origin() is called, the stack looks like this:

 char buf[256]     tmp4[100] tmp3[100] tmp2[100]  tmp1[100] EBP EIP 
[bbbbbbbbbbbbbbbbbb[PII_REDACTED][PII_REDACTED][PII_REDACTED][PII_REDACTED]1BBBBIIII] -> 0xbfffffff

There is an 8k buffer called 'reply' on the heap. Its purpose is to hold the entire
reply from the  server. It is populated by repeated calls to read(2), each call
reading 256 bytes into buf[] which are then concatenated into reply[]. Incedentally, 
no bounds checking is done on reply[], making it possible to cause a heap overflow:

count = 0;
        while ((n = read(s, buf, sizeof(buf))) > 0) {
            strcpy((char *)&reply[count],(char *)buf);
            count += n;
        }

After reading the entire reply into reply[], get_origin() then parses the contents;
this is where the lack of bounds checking becomes apparent:

rp = (char *)reply; 
        origin[0]='\0';
        reply[MAXREPLYLEN-1]='\0';

        rp = (char *)strstr(rp,tmp2);   
        while (rp != 0) {               
                                        
           pp = (char *)strstr(rp,tmp3);        
           if (pp == 0) {               
              prefix = 0;               
           } else {
              prefix = atoi(pp+1);      
           }

           if (prefix >= best_prefix) { 
              i = (char *)strstr(pp,tmp);       
              if (i != 0) {                     
                 i += strlen(DATA_DELIMITER);   
                 i++;                           
                 while (*i == ' ') i++;         
                 
                 j = i;                         
                 while (*j >= '0') j++;	// CHAR FILTERING
                 if (prefix > best_prefix) {
                    strcpy(origin,"/");         
                    best_prefix = prefix;               
                 } else {
                    strcat(origin,"/");         
                 }
                 strncpy(tmp4,i,(j-i)); // OVERFLOW
                 tmp4[j-i] = '\0';              
                 if (!(strstr(origin,tmp4))) {  
                    strncat(origin,i,(j-i));    
                 } else {
                    if (prefix == best_prefix)  
                       origin[strlen(origin)-1] = '\0';
                 } 
              } 
           } 
           rp = (char *)strstr(rp+1,tmp2);      
        }  

get_origin() finds the word 'route:' in reply[], then reads the number that follows 
it. If the number is greater than best_prefix (zero), then get_origin() continues to 
parse the reply[] buffer. It sets two pointers (*i, *j) to just past the location of 
the string 'origin:', and then increments *j until a character < ASCII '0' is found. 

So, *i marks the start of the buffer to copy into tmp4[] and *j marks the end of the
buffer. Because tmp4[] is 100 bytes long and it is possible to construct a reply of 
arbitrary length, it is trivial to overflow tmp4[], tmp3[], tmp2[] and tmp1[], over-
writing values on the stack.

To exploit this overflow is not quite that simple, however. To redirect the flow of
execution, the EIP saved on the stack needs to be overwritten with a value such as
0xbfff4567; the problem is that while the chars 0x67 and 0x45 pass the filter
mentioned above (*j >='0'), the chars 0xbf and 0xff do not (j is of type 'char'. Valid
values that pass through the filter are 0x30 -> 0x7f). If 0xffbf was to be embedded 
into the reply[] buffer as part of the overflow data, processing of the reply would 
stop and the tmp4[] buffer would not be overflowed.

This means that we cannot directly affect EIP. That leaves EBP. Again we face the same
problem: we can only overwrite EBP with values in the range 0x30 -> 0x7f.... and one
other: NULL (0x00). The NULL byte cannot pass through the filter if placed there by an
attacker, but it doesn't matter because get_origin() NULL-terminates the tmp4[] buffer
for us.

So, it is possible to do an off-by-one attack (or off-by-two; more on that later) by
using the NULL byte to overflow the least-significant byte of the saved EBP. There's
only one more problem to overcome: we still need to get a malicious EIP value onto the
stack somewhere it can be reached via an off-by-one attack. However, we can't place
the EIP into the exploit buffer, because the 0xbfffxxxx will not pass through the filter.
Luckily, the reply[] buffer is populat