Bugtraq mailing list archives

Exploit for CVS double free() for Linux pserver


From: Igor Dobrovitski <noident () mad scientist com>
Date: Sun, 2 Feb 2003 22:27:23 +1100 (EST)

   A bug in cvs versions up to and including 1.11.4 was recently found
where, under certain conditions,
a pointer is free()'d, and then free()'d again without being
re-initialised.
The reports with regards to the exploitability of the condition in
question range from -
"it is a classical exploitable double-free()" to "may possibly be
exploited".
I have written an exploit for Linux for pserver, and contrary to my usual
practice, decided to make it public.
First, I couldn't find any papers on the internet that would explain the
exploitation techniques of
double-free(), and I believe we don't have many publically available
exploits or in-depth discussion on the matter.
I hope that this little explanation that I've put together, and the
exploit itself may be somewhat useful
to the hacker/security community (we can't exist without each other, can
we? :)
The impact of a successful exploitation is not that great: an unprivileged
access to the system,
where your calls to getuid() will return a number that's far from 0 (cvs
drops provileges, and does it right).
The audience is expected to be familiar with D.L. malloc implementation.
The explanation of how D.L. malloc works can be found in two articles in
phrack 57.

If a request for a memory chunk is made, and if chunks that are kept in
linked lists, or the last remaindered chunk
cannot satisfy the requirement, the top memory chunk is split off, and a
chunk of the right size is returned
to malloc(). When this chunk is later free()'d, it may be coalesced with
other adjacent chunks if any of
the adjacent chunks are free. If not, the chunk is placed in a linked
list. After being processed by
the frontlink() macro, the linked list looks like this: we have two items
in the list, the bin and the chunk,
both BK and FD pointers of the bin point to the chunk, and both BK and FD
pointers of the chunk point to the bin.
Now, should this chunk be free()'d again, while on the linked list, the
picture changes. After the second free()
is called and the chunk is processed by the frontlink() again, we have
both BK and FD pointers of the bin still
pointed at the chunk, but both BK and FD pointers of the chunk will point
to itself !!!
Take a look now at the unlink() macro. This macro is called when taking a
chunk off the list:
#define unlink( P, BK, FD ) {            \
                             BK = P->bk;                          \
                             FD = P->fd;                          \
                             FD->bk = BK;                         \
                             BK->fd = FD;                         \
                         }
Remember that we have now P = P->bk = P->fd. What changes when this chunk
is passed though unlink()? Nothing!
This means that ALL subsequent calls to malloc of the size our chunk will
be returning the same chunk, the one that was double-free()'d. 
The rest is easy. After the chunk was double-free()'d, we make a request
to the program that will have to allocate
the double-free()'d chunk back to us, and copy the data we supply into the
memory returned to us.
Well, since the chunk is allocated, the backward and forward pointers are
not used, and user data gets straight there.
We will copy 2 addresses into the first 8 bytes of the chunk.
Now, we make another request to the program that will have to allocate to
us the same chunk. It will be passed through
the unlink() again, but this time, since the chunk is considered free, its
BK and FD pointers are used,
and lo and behold! We can overwrite any address in the memory with 4 bytes
of our choosing.


Now, how this particular exploit works:
   1. First we allocate a chunk of some size and make sure this chunk
comes from the top memory chunk.
Also make sure that this chunk stays allocated while we're
exploiting. This will keep our directory chunk
from being coalesced with the previous chunk.
   2. Allocate the Directory chunk, make sure it comes from the top memory
chunk.
   3. Allocate a chunk the same size as in step 1, for the same reason,
except that it will keep our Directory chunk
from being coalesced with the next chunk.
   4. Now that our exploitable chunk is secure, allocate a big chunk for
us to put shellcode, jumps and noops,
4K in this exploit.
   5. free() our directory chunk twice.
   6. Ask the server to malloc() a chunk of the size that was
double-free()'d, it will give us the very same
double-free()'d chunk without actually taking it off its linked list;
   7. the server will strcpy() our 2 addresses we provide into the first 8
bytes of our double-free()'d once
malloc()'ed chunk.
   8. Ask the server to again malloc() a chunk of the size that was
double-free()'d, upon which again our chunk is
malloc()'ed, passed through unlink(), overwriting memory.




Attachment: cvs_sploit.c
Description:


Current thread: