
7350cfingerd - cfingerd <= 1.4.2 remote root exploit


The bug is a rather trivial syslog format string vulnerability, and though
it is easy to spot, it is not that trivial to exploit. This is because to
the normal user we are only able to supply 60 arbitrary bytes. That is
not enough for the format string, the address to write to and the shellcode.
(from a low guess: 2 * strlen ("%12345u%98$n") + 2 * 4 + 15 = 47 bytes, to
 a high guess: 4 * strlen ("%123u%98$n") + 4 * 4 + 15 = 71 bytes, each having
 pros and cons, the first works not on old libc's, while it is short enough,
 the second works everywhere but is too long).
So we exploit this rather funny bug in cfingerd, too:

  sscanf (username, "%[^\r\n]\r\n", username);

We can inject a NUL byte anywhere into the username string where we want, by
constructing it like this:

username = "version\r<our-real-content>"

The \r is NUL'ed by the sscanf, and our content remains untouched through
the rest of the program flow, because the NUL byte terminates the string in
all the following check routines.

So we have saved us another additional 70 bytes :-)


-----


How to find the offsets:

You need to find three different values to make this exploit work. One is the
distance, one the return address and one the return address location. All
three have to be correct in order to get this to work. While the return
address and the distance have to be exact, the return address location can
be modified, see below.


The return address

 1. Install ltrace (from http://freshmeat.net/appindex/)
 2. On one terminal (term A from now) do:
    telnet localhost 79
 3. On another terminal (term B from now) do:

    export PID=`ps axuww | grep cfingerd | awk '{ print $2 }'`
    ltrace -o out -p $PID

 4. Switch to term A and enter:
    version<enter>

    where <enter> is equal to pressing the return key once ;-)

 5. Now look at the "out" file created by ltrace. In the second line or
    so there is an sscanf entry. The first parameter passed to sscanf is
    the return address buffer. This is not really correct, because we
    have to return 24 bytes after this address, so see the source, after
    each address there is a "+ 24" statement.


The return address location

 1. objdump --dynamic-reloc /usr/sbin/cfingerd | grep exit

    for the GOT exit() address (you can choose among strncmp, syslog, printf,
    fflush, and exit, in this order they are called)

It's impossible to take control by overwritting the return address stored on
the stack, because the program terminates always with the exit() function
when we user an internal query. It is possible by querying a user or service
perhaps, but it's too complicated anyway. By using the GOT we circumvent
stack-watching protections and have less trouble getting the address.


The distance

To find the distance is the most complicated thing. You can use some tricks,
such as trying %60$08x%64$08x%68$08x%72$08x up to 100 and using some unique
recognizeable pattern in the identd buffer, such as @@@@@@@@@ or _______,
so you will see the result as 40404040 or the like in syslog once you hit the
correct space. You've to multiply the distance with four afterwards (eg 4*87
is 348). Try around these values:

  348 for 1.4.*
  280 for 1.3.2

If you need help, take a look at the tools/ directory.


Thanks for testing and help goes to wilkins :-)

ciao,
scut

