Skip to content

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

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:

  • 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
  1. 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)
    • 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)
    • 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
  2. Then call free on the object.
    • This will set the buffer + 0x18 to a pointer that points to the start of the buffer.
  3. 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

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