[LWN Logo]
[Timeline]
Date:         Thu, 28 Sep 2000 23:33:28 +0100
From: Chris Evans <chris@FERRET.LMH.OX.AC.UK>
Subject:      Very interesting traceroute flaw
To: BUGTRAQ@SECURITYFOCUS.COM

Hi,

CREDIT
======

I'm starting with a credit section because I did not discover this
flaw. The flaw was discovered by Pekka Savola <pekkas@netcore.fi>, who
noted that traceroute could be caused to crash, which is pretty suboptimal
behaviour for a suid-root program :-) I took this forward and speculate
that in fact this very minor code flaw may well be exploitable.

VERSIONS AFFECTED
=================

(Where LBNL = Lawrence Berkeley National Laboratory)

Affected: LBNL 1.4a5
Safe:     LBNL 1.4a7
Safe:     RedHat7.0 traceroute (1.4a5 + a patch)

DISCUSSION OF FLAW
==================

First, some background reading, namely Solar Designer's excellent
discussion on the generic exploitation of heap overflows;

http://www.securityfocus.com/archive/1/71598

The discussion shows nicely how heap mismanagement is fatal. However,
overflowing a malloc()'ed buffer is not the only bad thing you can do to
the heap. In the case of traceroute, there was a reliable way of making
traceroute call free() on a pointer that was not obtained with malloc().

This flaw in traceroute (if your version is vulnerable) is tickled like
this:

traceroute -g 1 -g 1       (I think it didn't need a hostname)
Segmentation fault

Looking at the code, there is a file "savestr.c", which contains a
function savestr(). This savestr() function is essentially a
strdup() function, but with the difference that an attempt is made to cut
down on the number of malloc() calls. This is accomplished by malloc()'ing
a large block and handing out pointers _inside_ this block as savestr() is
repeatedly called.

So where does this all go wrong? Unfortunately, the clients of the
savestr() method seemed to treat savestr() like it was strdup() - i.e. all
pointers returned must be free()'d or you have a leak. This is not the
case, so we have the flaw: free() called on a pointer not allocated by
malloc().

Let's have a look at some cheesy ASCII diagram showing the block of memory
allocated by savestr():

         -----------------------------------------------
         |                                             |
         -----------------------------------------------
         ^                   * ^
         |                     |
       address               free()
       returned              called
       by malloc()           here (2nd
       and 1st savestr()     savestr call
       call                  result)


The reason this is so serious is that the free() call will attempt to do a
bit of free chunk management. This involves memory writes. The memory
writes are controlled by a "malloc chunk" descriptor, which resides just
before the address returned by malloc(), and passed by free(). Looking at
the above diagram, the location of the descriptor used by the faulty
free() called is marked by a "*". Bad news - that's in a region of memory
controlled by the malicious user.

See the above link to Solar's advisory for better discussion on how
control of the contents of a malloc chunk descriptor may be used to
subvert a program.

EXPLOIT
=======

No exploit is currently known, although it is believed than an exploit
could well be possible. I'm not an exploit dude, otherwise I'd have had a
good go!

If anyone can produce an exploit, please post it and claim lots of kudos
:) A working exploit would be a chilling reminder that the slightest flaw
in a security sensitive program is fatal, even if the flaw looks harmless
at first glance.

SMALL RANT
==========

If an exploit _is_ produced, it'll gain root access. Ouch. This really
should not be the case. A sensible and fault tolerant traceroute solution
should allocate a raw socket and then drop privileges on startup (quite a
few do this). This limits the severity of the hole to being able to gain a
raw socket. Not good but infinitely better than a root compromise.

RedHat-7.0 includes a patch to get a raw socket then drop privs at
startup. Hopefully this will be hoovered up into the upstream traceroute
source tarball. It does not appear to be in version 1.4a7

FOOD FOR THOUGHT
================

Many classic memory management flaws may be exploitable with a little
effort. Of particular note, calling free() twice on the same pointer is
not uncommon. This will be an exploitable flaw if, after the first
free() call, the memory is re-allocated and filled with user controlled
data.


Cheers
Chris