Linux Heap Internals
Linux Heap Internals
Linux Heap Internals
memeda@0ops
[email protected]
1
BACKGROUND
3
CATALOGUE
Introduction to GLIBC Heap
free()
malloc()
main_arena
Examples
4
Introduction to GLIBC Heap
5
INTRODUCTION
GLIBC Heap Structure Chunk
PREV_SIZE SIZE
Mem
FD BK
Logical description
USER DATA
Next
Chunk PREV_SIZE SIZE
In memory
(x86_64 ELF)
6
INTRODUCTION
LABEL Mem makes sense only when this chunk is malloc()’d by user.
&Mem is considered as the return value of malloc().
7
INTRODUCTION
For chunks of certain size range, there is a free list which is a linked list.
FD represents the forward pointer to the next chunk in the linked list.
The above two fields only make sense when the chunk is free()’d.
User’s data is stored in the region which starts at LABEL Mem. Notice
that the region includes PREV_SIZE of the next chunk.
8
View Heap as an Attacker
free()
9
CORRUPT FREE I
10
PROTOSTAR HEAP 3
11
PROTOSTAR HEAP 3
In old version of GLIBC malloc, there is function called unlink():
AN ARBITRARY WRITE!
12
PROTOSTAR HEAP 3
Then the problem comes, why unlink() is called when 3 free()s called? Since free()
means linking the chunk into the free list.
THE ANSWER is when GLIBC free a chunk, it will going to see whether the chunk
before/after is free. If it is, then the chunk before/after will be unlink()’d off its
double-linked list and these two chunks merge into one chunk.
Another question about this issue is DEF CON CTF 2014 Qual BabyHeap.
13
PROTOSTAR HEAP 3
So for this problem, you can overflow chunk a and overwrite the
heap header of chunk b.
You can also set PREV_SIZE of chunk b to fool GLIBC where the
chunk b begins. Craft a fake chunk b there and get an arbitrary
write. :D
14
MODERN UNLINK
[!] New checking: FD->bk == P && BK->fd == P
15
CORRUPT FREE II
Let us have a look at Problem stkof from HITCON CTF 2014.
There is a global array which records the pointer of every chunk you malloc()’d.
You can write arbitrary long content into the pointer in the global array.
We can also writing anything long into the data area these pointers pointing to.
17
CORRUPT FREE II
FD->bk ?= P
BK->fd ?= P
https://github.com/acama/ctf-writeups/blob/master/hitcon2014/stkof/x.py
20
CORRUPT FREE II
As you can see, the exploit overwrite chunk 3, place a fake chunk at the location of chunk 4.
The fake heap header of chunk 4 fool GLIBC to believe that the chunk before chunk 4 is
chunk 2.
There is a fake chunk 2 at chunk 2’ Mem. Notice that 0x602150 is the pointer in the global
array which pointing to chunk 2’s Mem(key point here to bypass the check!).
Call free(4) to unlink fake chunk 2(I explained before why this time chunk 2 is to be
unlinked). Then after unlinking, an address in the range of &global_array itself is written into
the global array. That means we can rewrite the content of the global array directly.
If we control the global array, then we can control which pointer we can write into.
This is a case about heap overflow where SPECIFIC WRITE turns into ARBITRARY WRITE!
21
CORRUPT FREE II
22
POISONED NULL BYTE
In August, Project Zero released a post about a GLIBC NULL byte off-by-
one exploitation.
The null byte will clear all the status bits of the SIZE of the next chunk.
When we try to free the next chunk, GLIBC is cheated to think the current
chunk is free and then unlink it. I mentioned this scenario just before.
The PREV_SIZE is actually the user data of the chunk but now it will used
to locate the chunk’s position relative to the next chunk.
The exploit described in the post is a local unprivileged exploit with Linux
32bit, which means we could half-disable ASLR and things become easy.
23
POISONED NULL BYTE
The exploit referred in the post used fd_nextsize and bk_nextsize to do the arbitrary write which is also a way besides
directly use fd and bk. Two things:
[1] P->fd_nextsize should not be NULL
[2] FD->fd_nextsize shouldd not be NULL
However, the target in the post is Fedora where that two asserts do not exist. When it comes to Ubuntu, ;-(
24
View Heap as an Attacker
malloc()
25
CHEAT MALLOC I
The head of the free list is in main_arena which I’ll cover it later.
Different sizes of fastbins also have their free list, but this time it
is a single-linked list.
As you can see later, the single-linked list makes things much
easier.
27
CHEAT MALLOC I
This picture shows two fast bins in
the free list which both have a size of
0x30.
It just mainly pick out the first (->fd) fast bin in the linked list, and
return it to the user.
That means if we control just one node of this linked list, then we
can cheat GLIBC to malloc the chunk where specified by us,
right?
How to do that?
29
CHEAT MALLOC I
Let us malloc two fast bins. (Btw, thanks to ricky zhou@PPP for his exploit)
2) Overflow the 1st fast bin to change 2nd fast bin’s FD to our specified value.
3) Malloc one time. GLIBC return the 2nd fast bin to us, meanwhile the header of
the single-linked list points to our specified value. The value points to a fake fast bin
entry we crafted already.
5) Then just write into the new chunk which birth at the specified place you want to
and finish your exploit.
30
CHEAT MALLOC I
The only check for the fast bin entry in the list is the SIZE! Craft a
proper size for the fake fast bin entry.
Recall the problem STKOF, where the fake fast bin could be?
If you can free any where you want, then just free the place you
could write(craft) directly. You don’t necessarily need to work on
the heap and do the overflow.
32
CHEAT MALLOC II
One may ask whether I could cheat malloc() with normal bins
and double-linked list.
Let us first check out how does malloc() work this time?
33
CHEAT MALLOC II
I malloc 7 chunks
with normal size
and free 3 of them.
As you can see,
they are both in one
double-linked list.
Then I do
malloc(530).
34
CHEAT MALLOC II
35
CHEAT MALLOC II
At first, all the free()’d chunks are put into unsort bin list.
If the size is fit, then unlink this chunk and return to the user.
If not, put this chunk into its free list according to its size.
And after this, you are able to cheat GLIBC to malloc at the
place you want(in this case 0xe10000). But during this, Chunk
0xe10000 will be unlinked so make sure its BK->FD points to
0xe10000 this time.
For this case, I just bring out my ideas. Let me know if you have
other nice ways to exploit it.
38
CHEAT MALLOC III
That means the top chunk usually has a large SIZE and be located
at the bottom of the heap (behind all the normal heap chunks).
39
CHEAT MALLOC III
Malloc_Des-Maleficarum also described a trick which cheats malloc by using the top chunk.
[1] You can use overflow to change the SIZE of the top chunk to 0xffffffff(Linux x86).
[2] Then you just malloc, and if the control flow goes into use_top, then actually you can
malloc whatever large size you want.
[3] You can craft a special size s. Then after malloc()ing, the top chunk’s address will change
to the original address plus s. In fact, we can specify any new address we want by specifying
s.
[4] If we malloc again, then we could get a chunk at the special place we want.
This trick may not make sense when meet ASLR since at most time the location of the top
chunk cannot be predicted.
40
View Heap as an Attacker
main_arena
41
MAIN_ARENA
It’s the time for us back to the most basic and important
structure main_arena.
42
MAIN_ARENA
main_arena is defined as below(Linux x86_64):
Array fastbinY
top
Array bins
next
system_mem
How to use heap overflow to rewrite it? I’ll talk about it later.
46
FAKE MAIN_ARENA
The main idea is to specify the head of the linked list in the arena,
and then cheat GLIBC to malloc a new chunk at that specified
place we prefer.
But things are not very easy. The specified place(the fake chunk)
must satisfy many conditions, otherwise malloc will go to
failure. ;-(
47
FIGHTING WITH ASSERTIONS
48
FIGHTING WITH ASSERTIONS
And it is very hard to control the value which address (ptr &
~(HEAP_MAX_SIZE - 1)) pointing to.
So in a word, the best choice is to satisfy [II] and then bypass this assert. 50
BTW…
To satisfy [11], you faked chunk’s SIZE should have 0x2 bit
(mmapped).
If you deep into _int_malloc, you will find out that sometimes
before it return p; to exit, it will finally reset the head of the chunk
which it will then return to you, which means you’ll lose the 0x2
bit.
52
FIGHTING WITH ASSERTIONS
a free()’d candidate chunk in unsort bin free list and the size of
free()’d candidate chunk in the free list is exactly the same as
our crafted chunk’s size.
53
FIGHTING WITH ASSERTIONS
Again, fastbin becomes our first choice, since we must deal with FD
and BK when use whether normal small bin or normal large bin.
[1] A crafted chunk you want to malloc on which has a proper SIZE
(0x2 bit set).
[II] The entry of fastbinY’s free list should be set to the address of
your crafted chunk. (&SIZE - 0x8 on Linux x86_64).
54
A FAKE ARENA EXAMPLE
You do not need to specify the whole 0x888 bytes. For example, if you use fast bin to exploit, you
may just specify the first several bytes. The picture above is for Linux x86_64. 55
DEFEAT ASLR
If the size you want to malloc is not less than 128KB, then GLIBC
may use mmap() to allocate a new area for you.
There are many gaps in the program’s VM map, and GLIBC will
use these large gaps to satisfy your malloc request.
56
DEFEAT ASLR
main_arena is in the marked area(TLS is there), and if we can malloc a chunk before this area and
overflow it, then we can overwrite main_area. 57
DEFEAT ASLR
58
DEFEAT ASLR
Note that all the malloc()s in [1]&[2] will actually call mmap().
59
_nl_global_locale
arena
DEFEAT ASLR
The chunk just before arena is located at 0x7f0313248010. And with no surprise, the pointer of arena
is at 0x7f031326a700. I am able to overflow it for sure. Btw, the stack canary is at 0x7f031326a768. 60
DEFEAT ASLR
61
DEFEAT ASLR
mov 0x68(%r8),%r15
testb $0x20,0x1(%r15,%rax,2)
62
DEFEAT ASLR
OK! Now just replace the pointer of the arena with an address
which a fake arena structure is located at there.
BUT! Due to ASLR, you may not know the exact address of your
fake arena structure ;-(
63
DEFEAT ASLR
Actually there are two gaps and the location of these two gaps are easy to predicted.
64
DEFEAT ASLR
And finally the chunk will be located between 0x10000 - 0x400000 and
0x401000 - 0x601010.
As you can see, I get two chunks, one is at 0x10000 and one is at
0x401000.
* You can just put the fake arena and the fake _nl_global_locale in the
0x10000 chunk and then overwrite the pointer of the arena to
0x10010 (0x10000 + 0x10 is the beginning of the user data).
65
FURTHERMORE
http://217.logdown.com/posts/241446-isg-2014-pepper
66
View Heap as an Attacker
67
OWN THE PAGE
68
OWN THE PAGE
If you could overwrite the SIZE of a chunk, set its 0x2 bit and any
large size you want, then free it!
74
OWN THE PAGE
What will happen if we malloc(1052600) then?
:D Non-writable page 0x601000-0x602000 will never be existed. Just overflow it to write GOT! 75
OWN THE PAGE
And keep in mind that the fake SIZE of the chunk you set must
be times of a page size, due to ASSERT ;-(.
76
RESOURCES
https://code.google.com/p/google-security-research/issues/detail?id=96
There are some ‘null byte off-by-one on heap’ war-game style programs, which are all
good materials to exercise Linux heap pwning.
CTF Pwnables
STKOF https://github.com/hitcon2014ctf/ctf/raw/master/
a679df07a8f3a8d590febad45336d031-stkof
OREO https://github.com/lovelydream/CTF/blob/master/
oreo_35f118d90a7790bbd1eb6d4549993ef0
PEPPER https://github.com/lovelydream/CTF/blob/master/
pepper_e87791048cc540b725046a96d6724d8b
77
END…
I’ve tried my best to refer all the ways I know to exploit the heap
overflow with the newest version of GLIBC.
Basically I do not consider PIE in the slides but ASLR & NX.
If you have any other fantastic heap exploit tricks, please share
with me.
[2] http://acez.re/
[3] https://rzhou.org/~ricky/hitcon2014/stkof/test.py
[4] https://www.blackhat.com/presentations/bh-usa-07/Ferguson/Whitepaper/bh-usa-07-ferguson-WP.pdf
[5] http://217.logdown.com/posts/241446-isg-2014-pepper
[6] http://googleprojectzero.blogspot.sg/2014/08/the-poisoned-nul-byte-2014-edition.html
[7] http://sebug.net/paper/phrack/66/p66_0x0A_Malloc_Des-Maleficarum.txt
[8] http://www.phrack.org/issues/57/8.html
❤Team 0ops
@Blue-Lotus Kelwin
80