Dlmalloc
Dlmalloc¶
- The original Linux glibc implementation until 2006
- Leaking Pointers allow an attacker to de reference pointers to get pointers into glibc and the heap
Functions¶
malloc - Allocates a chunk* at least size bytes and returns
1. If size is less than or equal to a fast bin or a small bin use it.
2. Move fast bins to unsorted bins
3. Merge adjacent freed chunks together
4. Sort bin
5. Search through best fit bins
6. Split the Top
free - De-allocates chunk* pointed by p
1. If chunk is a fast chunk then put it in the fast bin
2. Else
- Merge adjacent freed chunks together
- Then put it into the Unsorted bin
realloc - Increases the size of a chunk* while preserving it's content
calloc - malloc but clears data
Structures¶
- Chunks must be at least 0x20 bytes
- Since Chunks are 8 byte aligned the 3 Least significant bits are used for Flags
- Bit 0: previous chunk in-use (
prev_inuse
)- If the prev_inuse is not set then malloc will use prev_size
- Bit 1: Was the chunk allocates with mmap. (
is_mmapped
) - Bit 2: is allocated from a spawned thread?
non_main_arena
- Bit 0: previous chunk in-use (
Allocated Chunk:
size | user-data ....... |
Freed Malloc Chunk:
prev_size | size | next_chunk_pointer | prev_chunk_pointer | fd_nextsize | bk_nextsize | ... | size |
Bins¶
Type | Size | List Type | Info |
---|---|---|---|
Fast | [0x20 - 0x80] | Single Linked | prev_inuse is set |
Unsorted | All Sizes | Double Linked | chunks before sorted |
Small | [0x20 - 0x400) | Double Linked | |
Large | Ranges | Two Level Double Linked |
Arena¶
Main Arena:
- Is a global variable in the program
- Contains the pointers to the bins
- top
points to "wilderness"
Arena/malloc_state:
mutex | flags | fastbins[] | top | remainder | bins[] | ... |
unsorted | small[] | large[] | ||||
Thread Arena:
Unsafe Unlink Arbitrary Write¶
- Works on Glibc version < 2.26
- Because the freed memory thinks that the previous block is also freed memory it combinds the data together to a bigger chunk.
- With this confusion of data this allows the unlink macro to be run.
- This results in arbitrary write
- Use a buffer to overwrite the heap meta data for an object that has a size greater than 0x70 bytes.
- We set up the prev_chunk_pointer to the pointer to the global buffer address - 0x10.
- This makes it so that when dereferenced the prev_chunk_pointer points to the global buffer address. (
(buffer->prev_chunk_pointer)->next_chunk_pointer == buffer
)
- This makes it so that when dereferenced the prev_chunk_pointer points to the global buffer address. (
- We set up the next_chunk_pointer to the pointer to the global buffer address - 0x18.
- This makes it so that when dereferenced the next_chunk_pointer points to the global buffer address. (
(buffer->next_chunk_pointer)->prev_chunk_pointer == buffer
)
- This makes it so that when dereferenced the next_chunk_pointer points to the global buffer address. (
- We set the prev_size (originally 0x90 because of metadata) to a smaller value that falls within the . Usually the size without the metadata (0x80).
- Using the buffer address we overwrite the previous_in_use flag to false
- We set up the prev_chunk_pointer to the pointer to the global buffer address - 0x10.
- Then call free on the object.
- This will set the
buffer + 0x18
to a pointer that points to the start of the buffer.
- This will set the
- This can be exploited by changing the pointer in
buffer + 0x18
and then writing to the buffer.- This will dereference the pointer in
buffer + 0x18
and write data to that address
- This will dereference the pointer in
Fixes¶
- Two new security checks include corrupted size vs. prev_size and corrupted double-linked list
Example¶
Fastbin Consolidation Double Free¶
- Works on Glibc version < 2.30
- Double Free check is bypassed when a large chunk is allocated between the two frees.
- This makes the same address be in the fastbin and the unsorted bin.
- When making new allocations
Vulneable Example:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main() {
void* p1 = malloc(0x40);
void* p2 = malloc(0x40);
fprintf(stderr, "Allocated two fastbins: p1=%p p2=%p\n", p1, p2);
fprintf(stderr, "Now free p1!\n");
free(p1);
void* p3 = malloc(0x400);
fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3);
fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n");
free(p1);
fprintf(stderr, "Trigger the double free vulnerability!\n");
fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n");
fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", malloc(0x40), malloc(0x40));
}
House of Spirit¶
- Make a fake chunk and change the pointer to be freed.
- This allows the same control as the Unsafe Unlink.
- index 0 is the Pointer to write data to
- index 3 is the data that write to that address