Fandango on free()
I apologize for the delay in keeping this blog updated. As I was working in the solution for the 9th level of the
Advanced Buffer Overflows
challenge I had some serious troubles with the different implementations of Doug Lea's Malloc (dlmalloc from now on) management system which I thought would do for a good article. Althought the solution for abo9 is in its final stages, I'll elaborate on the heap implementation this time.
Lots of good articles have been already written in this subject (Which I'll link at the end of this post for anyone that's interested) but with the versions I was using in my VMs as lab I had more than one headache(In fact, vulnerable versions of glibc seem to be already patched in SuSE versions 9.X. In my case switching to Red Hat 9.0 did the job). None of the popular papers seemed to work in my case, hence, I decided to dig myself in the inner workings of this dynamic memory allocation system. Instead of explaining a C implementation I went for a Reverse Code Engineering approach which turned as follows. In this post I won't explain the inner workings of dlmalloc, that's already been nicely documented in other papers such as
Vudo - An object superstitiously believed to embody magical powers
by Michel "MaXX" Kaempf, hence I'll avoid any reference to that.
The function in question is
free()
which takes care of freeing a used chunk once we have finished our work with it.
free()
is actually a wrapper around
_int_free()
which does the real job.
_int_free()
makes some checks and then makes use of the
unlink()
macro to unlink two adjacent chunks and join them to create a larger one. You can get the interesting part of the disassembly of
_int_free()
HERE.
At address
0x42074464
it loads the pointer to the chunk we want to free into the
edi
register. In
0x4207446b
loads the address of the
prev_size
field of the
buf2
buffer into
ecx.
Right in the next intruction the contents of size field of
buf2
are copied into
eax.
After check int the
arena
struct we jump to
0x420744a0
at
0x42074481.
In the next two instructions it checks wether or not the 2nd least significant bit of the
size
field is enabled and if it is(which is not our case) it jumps to another region within
_int_free().
Now is where the fun part for exploitation begins. At
0x420744ad,
we calculate the address of the next chunk using our address + the
prev_size
field of buf2 (which we can control overflowing the first
buf1)
and store it in
eax,
which can now contain the address of the
prev_size
of a fake chunk we can create. In the next instruction we load the
size
field of our fake chunk into
edx.
After some saving into local variables, some 8 byte boundary roundings and a
PREV_INUSE
checks, we arrive to
0x420744c8.
Here we load in
eax
the
prev_size
of
buf2
and in the next instruction we store in
ecx
the address of
buf2's
prev_size
minus the
prev_size
of
buf2
itself. This hypothetically would get us the previous chunk's
prev_size
address but since we manipulated
buf2's
prev_size
we can make it go wherever we want.
So, since
ecx
now points to the
prev_size
of our fake chunk, let's talk a little bit about the
unlink()
macro. This macro basically swaps the
fd
and
bk
fields of unused chunks to make the use of free space in memory more efficient and to avoid fragmentation. Being a macro as it is, is compiled inline with the rest of the code and, since doesn't provide any sanity checking it'll swap some addresses we control with information we control allowing us to overwrite 4 bytes wherever we want in memory with the information we want. You can see
unlink()
in action in address
0x420744cd,
0x420744d2,
0x420744d5
and
0x420744d8.
From then on the function continues his normal path as the damage has already been done.
Well that's it. I'll talk about exploitability and it's techniques in the next post. If anybody felt this was quite messy to follow I apologize; I understand it's a difficult matter (at least it was for me :P) but I hope somebody else learned something from this. I also hope I can bring a final solution for abo9 in the upcoming days. As promised I include some references to previous works in the topic here:
- Once upon a free() by Anonymous
- Vudo - An object superstitiously believed to embody magical powers bt Michel "MaXX" Kaempf
- w00w00 on Heap Overflows by Matt Conover and the w00w00 Security Team
- Remedial Heap Overflow (Video from DEFCON 15) by atlas