Buffer Overflows on the Heap(1)
π©βπΎ: ???????????????????????????????
Chapter 4 - Dynamic Memory Management(4.65)
- Book by Robert C. Seacord
Buffer Overflows on the Heap
Buffer overflows in the heap are not always appropriately addressed, and developers adopt solutions that protect against stack-smashing attacks but not buffer overflows in the heap.
- the unlink technique
- the frontlink technique
- PREV_INUSE bit
The attacker may:
- provide the address of the return pointer on the stack, and use the
unlink()
macro to overwrite the address with the address of malicious code. - overwrite the address of a function called by the vulnerable program with the address of the malicious code.
- Buffer overflow overwriting boundary tag
The most difficult part of this exploit is determining the size of the first chunk so that the boundary tag for the second argument can be precisely overwritten. To do this, an attacker could copy and paste the request2size(req,nb) macro from dlmalloc into his or her exploit code and use this macro to calculate the size of the chunk.
The unlink Macro
1. #define unlink(P, BK, FD) { \
2. FD = P->fd; \
3. BK = P->bk; \
4. FD->bk = BK; \
5. BK->fd = FD; \
6. }
How it works
- To forge the pointer to node, attacker can get a chance to read/write memory:
flink
andblink
on Windowsfd
andbk
on Linux
Targets:
- Function return address
- Exception Handling ?
- Important function call address
Frontlink: When malloc
Suppose there are 3 variables need memory in the heap: buf0, buf1, and buf2.
1 buf0 = malloc(32); <-- //buf0 is allocated with chunk0
2 read(0, buf0, 64);
3 buf1 = malloc(32);
4 buf2 = malloc(32);
Unlink:
int remove (ListNode * node)
{
node -> bk -> fd = node -> fd; // (node -> fd) is (malicious payload)
node -> fd -> bk = node -> bk; // (node -> bk) is (arbitrary address)
return 0;
}
When buf0 is attacked, The attacker can overflow buf0’s memory chunk0 to modify chunk1’s fd
and bk
. When chunk1 is used (removed from the link) at Line 3, it becomes:
int remove (ListNode * node)
{
arbitrary_address -> fd = malicious_payload; // ATTACK !
malicious_payload -> bk = arbitrary_address; // actually no need
return 0;
}
The attacker:
- Supplies the address of a memory chunk and not the address of the shell code.
- Arranges for the first four bytes of this memory chunk to contain executable code.
Unlink: When free
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]); // ATTACK: can overwrite the boundary tag!
free(first); <-- break // If the second chunk is unallocated, the free() operation will attempt to consolidate it with the first chunk.
free(second);
free(third);
return(0);
}
At the break point, To determine whether the second chunk is unallocated, free() checks the PREV_INUSE bit of the third chunk.
| P = x | 1st chunk (with content) | P = 0 | 2nd chunk | 3rd chunk |
In 2nd chunk’s head, there is a field called size of chunk
, which is used to deduce the address of the 3rd chunk. If overload 1st chunk and change the value of chunk2’s size of chunk
to be -4
, the system will think chunk2
is chunk3
by mistake!
In this way, second chunk is consolidated with the first chunk.
first = malloc(666);
Malicious Arguments:
first chunk
| content | filling bytes | even int | size | fd | bk |
|--shellcode--| xxxxxxxxxxxxx | 24 | -4 | fp-12 | addr | \0 |
|----------------668 bytes---------------|4 byte|4 byte |4 byte| \0 |
addr
is the malicious address to shellcode.- The
address - 12
is included in the malicious argument so that the unlink() method overwrites the address of thefree()
library call with the address of the shellcode.
Original unlink
:
int remove (ListNode * node)
{
node -> bk -> fd = node -> fd; // (node -> fd) is (fp-12)
node -> fd -> bk = node -> bk; // (node -> bk) is (addr to shellcode)
return 0;
}
So it becomes:
int remove (ListNode * node)
{
(addr to shellcode) -> fd = (fp-12);
(fp-12) -> bk = (addr to shellcode); // ATTACK!
return 0;
}
- The unlink() macro writes four bytes of data supplied by an attacker to a four-byte address also supplied by the attacker.
- Once an attacker can write four bytes of data to an arbitrary address, it is easy to execute arbitrary code with the permissions of the vulnerable program.
- Can execute arbitrary code with the permission of the vulnerable program
An attacker can:
- Can overwrite a Return address in stack with the address of the malicious code
first chunk
| content | filling bytes | even int | size | fd | bk |
|--shellcode--| xxxxxxxxxxxxx | 24 | -4 | ret | addr | \0 |
|----------------668 bytes---------------|4 byte|4 byte |4 byte| \0 |
- Overwrite the address of a function called by the vulnerable program with the address of malicious code.
first chunk
| content | filling bytes | even int | size | fd | bk |
|--shellcode--| xxxxxxxxxxxxx | 24 | -4 |func_addr| addr | \0 |
|----------------668 bytes---------------|4 byte| 4 byte |4 byte| \0 |
-
Examine the executable image to find the address of the jump slot for the free() library call.
-
Usually the frontlink technique is more difficult than the unlink one.
malloc_free_snip.c
...
/*
------------------------------ free ------------------------------
*/
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
...
else if (!chunk_is_mmapped(p)) {
...
nextchunk = chunk_at_offset(p, size);
...
nextsize = chunksize(nextchunk);
...
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(p, bck, fwd);
}
if (nextchunk != av->top) {
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
/* consolidate forward */
if (!nextinuse) {
unlink(nextchunk, bck, fwd);
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);
/*
Place the chunk in unsorted chunk list. Chunks are
not placed into regular bins until after they have
been given one chance to be used in malloc.
*/
bck = unsorted_chunks(av);
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))
{
errstr = "free(): corrupted unsorted chunks";
goto errout;
}
p->fd = fwd;
p->bk = bck;
if (!in_smallbin_range(size))
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;
set_head(p, size | PREV_INUSE);
set_foot(p, size);
check_free_chunk(av, p);
}
/*
If the chunk borders the current high end of memory,
consolidate into top
*/
else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
check_chunk(av, p);
}
...
}
Some Techniques
- The House of Prime
- The House of Mind
- The House of Force
- The House of Lore
- The House of Spirit
- The House of Chaos
π©βπΎ: My brain cells also overflow π€
Reference
- Security Coding in C and C++. by Robert C. Seacord. Chapter 4. ISBN-13: 978-0-321-82213-0
- Heap overflow using unlink
- Linux heap overflow - fastbin
- Understand Heap Overflow
- 0 day security - Software Vulnerabilities Analysis (Version 2).ISBNοΌ9787121133961.
- IS2620 - lecture 5