Dynamic Memory Vulnerabilities
Stack data lasts per function while heap memory is there until freed.
Kernel Space
Stack
Memory Mapping Region
Heap
BSS Segment
Data Section
Text Segment (ELF)
...
int main()
{
// Initial Variables declared
char *buffer = NULL;
int buffSize = 0;
//Getting size of the buffer
printf("Give me size of buffer: ");
scanf("%d", &buffSize);
// Allocating Buffer
malloc(buffSize)
//Get input and print it.
printf("Input: ");
scanf("%s", buffer);
printf("%s\n", buffer);
return 0;
}
For Linux, use the GNU Libc implementation
Potentially create a dynamic allocation around it, but not ideal.
Various Dynamic Allocators Implementations
Note: Only for small allocations. For large allocations, it uses mmap.
mmap is a system call in Linux that creates a new mapping in virtual address space in the calling process. Has six parameters:
Bit 0: PREV_IN_USE
Bit 1: IS_MMAPPED
Bit 2: NON_MAIN_ARENA
unsigned long mchunk_prev_size;
unsigned long mchunk_size;
Dynamically Allocated Memory Chunk
chunk_addr
mem_addr:
prev_size
prev_size
size
a[0]
a[1]
size
b[0]
b[1]
chunk1: int *a = malloc(0x8)
chunk1: int *b = malloc(0x8)
Note: Only applies if PREV_INUSE flag is set
prev_size
prev_size
size
a[0]
a[1]
size
b[0]
b[1]
chunk1: int *a = malloc(0x8)
chunk1: int *b = malloc(0x8)
Note: Only applies if PREV_INUSE flag is set
unsigned long mchunk_prev_size;
unsigned long mchunk_size;
Dynamically Allocated Memory Chunk
b[1]
unsigned long mchunk_prev_size;
unsigned long mchunk_size;
Cache-Specific Metadata
Chunks allocated by mmap are marked via the M bit.
From pwn.college
Bin #
prev_size
Chunk Size
FD Pointer
BK Pointer
Unused Space
prev_size
Chunk Size
FD Pointer
BK Pointer
Unused Space
Bin #
prev_size
Chunk Size = 10
FD Pointer
BK Pointer
Unused Space
prev_size
Chunk Size = 10
FD Pointer
BK Pointer
Unused Space
Bin #
prev_size
Chunk Size = 10
FD Pointer
BK Pointer
Unused Space
prev_size
Chunk Size = 64
FD Pointer
BK Pointer
Unused Space
Bin #
prev_size
Chunk Size = 10
FD Pointer
BK Pointer
Unused Space
prev_size
Chunk Size = 64
FD Pointer
BK Pointer
Unused Space
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
typedef struct tcache_entry
{
struct tcache_entry *next;
struct tcache_perthread_struct *key;
} tcache_entry;
The tcache entry is stored in user data (right after the header) in a freed chunk!
a = malloc(16);
b = malloc(16);
c = malloc(32);
d = malloc(32);
free(b);
free(a);
free(c);
free(d);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 0
count_32 = 0
entry_16 = NULL
entry_32 = NULL
tcache entry B
next: NULL
Key: NULL
tcache entry C
next: NULL
Key: NULL
tcache entry A
next: NULL
Key: NULL
tcache entry D
next: NULL
Key: NULL
a = malloc(16);
b = malloc(16);
c = malloc(32);
d = malloc(32);
free(b);
free(a);
free(c);
free(d);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 1
count_32 = 0
entry_16 = B
entry_32 = NULL
tcache entry B
next: NULL
Key: Mike
tcache entry C
next: NULL
Key: NULL
tcache entry A
next: NULL
Key: NULL
tcache entry D
next: NULL
Key: NULL
a = malloc(16);
b = malloc(16);
c = malloc(32);
d = malloc(32);
free(b);
free(a);
free(c);
free(d);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 2
count_32 = 0
entry_16 = B
entry_32 = NULL
tcache entry B
next: A
Key: Mike
tcache entry C
next: NULL
Key: NULL
tcache entry A
next: NULL
Key: Mike
tcache entry D
next: NULL
Key: NULL
a = malloc(16);
b = malloc(16);
c = malloc(32);
d = malloc(32);
free(b);
free(a);
free(c);
free(d);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 2
count_32 = 1
entry_16 = B
entry_32 = C
tcache entry B
next: A
Key: Mike
tcache entry C
next: NULL
Key: Mike
tcache entry A
next: NULL
Key: Mike
tcache entry D
next: NULL
Key: NULL
a = malloc(16);
b = malloc(16);
c = malloc(32);
d = malloc(32);
free(b);
free(a);
free(c);
free(d);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 2
count_32 = 2
entry_16 = B
entry_32 = C
tcache entry B
next: A
Key: Mike
tcache entry C
next: D
Key: Mike
tcache entry A
next: NULL
Key: Mike
tcache entry D
next: NULL
Key: Mike
a2 = malloc(16);
b2 = malloc(16);
c2 = malloc(32);
d2 = malloc(32);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 2
count_32 = 2
entry_16 = B
entry_32 = C
tcache entry B
next: A
Key: Mike
tcache entry C
next: D
Key: Mike
tcache entry A
next: NULL
Key: Mike
tcache entry D
next: NULL
Key: Mike
a2 = malloc(16);
b2 = malloc(16);
c2 = malloc(32);
d2 = malloc(32);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 1
count_32 = 2
entry_16 = A
entry_32 = C
tcache entry B
next: NULL
Key: NULL
tcache entry C
next: D
Key: Mike
tcache entry A
next: NULL
Key: Mike
tcache entry D
next: NULL
Key: Mike
a2 = malloc(16);
b2 = malloc(16);
c2 = malloc(32);
d2 = malloc(32);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 0
count_32 = 2
entry_16 = NULL
entry_32 = C
tcache entry B
next: NULL
Key: NULL
tcache entry C
next: D
Key: Mike
tcache entry A
next: NULL
Key: NULL
tcache entry D
next: NULL
Key: Mike
a2 = malloc(16);
b2 = malloc(16);
c2 = malloc(32);
d2 = malloc(32);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 0
count_32 = 1
entry_16 = NULL
entry_32 = D
tcache entry B
next: NULL
Key: NULL
tcache entry C
next: NULL
Key: NULL
tcache entry A
next: NULL
Key: NULL
tcache entry D
next: NULL
Key: Mike
a2 = malloc(16);
b2 = malloc(16);
c2 = malloc(32);
d2 = malloc(32);tcache_perthread_struct -- Mike
tcache_perthread_struct -- Mike
counts:
entries:
count_16 = 0
count_32 = 0
entry_16 = NULL
entry_32 = NULL
tcache entry B
next: NULL
Key: NULL
tcache entry C
next: NULL
Key: NULL
tcache entry A
next: NULL
Key: NULL
tcache entry D
next: NULL
Key: NULL
Added the concept of safe-linking
Credit to pwn.college
Credit to pwn.college
int main()
{
char *some_data = malloc(256);
scanf("%s", some_data);
printf("%s\n", some_data);
char really_long_string[512] = {'A'}
strncpy(
some_data,
really_long_string,
strnlen(really_long_string, 512));
free(some_data);
return 0;
}int main()
{
char *some_data = malloc(256);
scanf("%s", some_data);
printf("%s\n", some_data);
// Make sure it is freed.
free(some_data);
scanf("%s", some_data);
if (strncmp(some_data, PASSWORD, 256))
{
printf("I am authenticated\n");
}
return 0;
}int main()
{
char *some_data = malloc(256);
scanf("%s", some_data);
printf("%s\n", some_data);
/* You put code here */
/* End of your code */
return 0;
}int main()
{
char *some_data = malloc(256);
scanf("%s", some_data);
printf("%s\n", some_data);
free(some_data);
free(some_data);
return 0;
}int vuln()
{
char *buffer;
int size = 0;
printf("Size of buffer: ");
scanf("%d", &size);
// Allocating Memory
buffer = malloc(size);
printf("Input: ");
scanf("%s", buffer);
printf("%s\n", buffer);
// What happens here?
free(buffer);
printf("Print again: %s\n", buffer);
return 0;
}int vuln()
{
char *buffer;
int size = 0;
printf("Size of buffer: ");
scanf("%d", &size);
// Allocating Memory
buffer = malloc(size);
printf("Input: ");
scanf("%s", buffer);
printf("%s\n", buffer);
// What happens here?
free(buffer);
buffer = NULL;
printf("Print again: %s\n", buffer);
return 0;
}int vuln()
{
char *buffer;
int size = 0;
printf("Size of buffer: ");
scanf("%d", &size);
// Allocating Memory
buffer = malloc(size);
printf("Input: ");
scanf("%s", buffer);
printf("%s\n", buffer);
// What happens here?
free(buffer);
free(buffer);
return 0;
}Carefully creating a region of memory with heap-metadata in it can trick the heap allocator to think it's another chunk of memory!
What are some things we might need to include in the metadata structure?