Large Bin Attack
Explanation of large bin attack
Large bin attack.
This technique allows you to modify an arbitrary address with an value of a large bin chunk. This technique alone is not enough to get code execution, in modern GLIBC FSOP in needed to finish exploit, overwrite _IO_list_all to forge fake _IO_File structures and in older GLIBC you could overwrite global_max_fast and perform a fastbin poison/dup.
Large bin summary:
This is the structure for large bins.
1
2
3
4
5
6
7
8
9
10
11
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk, if it is free. */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
/* double links -- used only if this chunk is free. */
struct malloc_chunk* fd;
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize;
struct malloc_chunk* bk_nextsize;
};
typedef struct malloc_chunk* mchunkptr;
Large bins store chunks within a specific range and not of the same size. In order to speed up indexing and searching the fd_nextsize and bk_nextsize were introduced, these points are used to traverse chunks of the group size.
How does the attack work?
The attack happens in the process when a chunk is inserted into a large bin, which then is unlinked and pointers are changed in the relevant chunks.
The goal is to modify the bk_nextsize field of a chunk.
When GLIBC inserts a chunk into a large bin, it performs pointer updates. The goal is to hijack which pointers GLIBC updates, so that it writes an arbitrary address into a arbitrary memory location.
So why do we need to used chunks of different sizes?
When inserting a chunk into a large bin, glibc compares sizes:
1
2
if (size == chunksize_nomask(fwd))
fwd = fwd->fd; // insert second inlist, skip nextsize updates.
if the sizes are equal, GLIBC does not touch the nextsize pointers.
This attack targets the nextsize list, which is used when chunks are not of the same size but are still in the same large (within a certain range).
What actually happens during large bin insertion?
GLIBC <= 2.29
There are two possible cases depending on size:
- Inserted chunk is smaller
fwd->fd->bk_nextsize = victim;
- Inserted chunk is larger
victim->bk_nextsize->fd_nextsize = victim;
Either cases gives a write to one of the nextsize pointers.
GLIBC >= 2.30
Everything changed, as a new integrity check was introduced to prevent the second case (inserted chunk is larger) from working.
Only this case works:
- Inserted chunk is smaller than the smallest chunk
victim->bk_nextsize->fd_nextsize = victim;
This means GLIBC will write the address of the inserted chunk into bk_next_size + 0x20 because fd_nextsize is 0x20 bytes after the pointer to the chunk.
Exploitation Steps
Prerequisites
- Allocate a large chunk.
- Lets call it
chunkA
- Lets call it
- Allocate a chunk smaller than
chunkA. - Lets call itchunkB - Allocate a guard chunk to prevent consolidation.
- Free
chunkA.chunkAwill be placed in the unsorted bin.
- Allocate a chunk larger than
chunkA.- Lets call it
chunkC - This is done so that
chunkAis sorted into the large bin.
- Lets call it
- Free
chunkB - Now modify
chunkA- Modify
chunkA->bk_nextsizeto point to[target - 0x20]
- Modify
- Allocate a larger chunk than
chunkB.- This is done so that
chunkBcan be sorted into large bin, thenGLIBCwill try to update the pointers which leads to overwriting the addresschunkA->bk_nextsize->fd_nextsizewith the address ofchunkB
- This is done so that
- Now
target == chunkB
Demo
Comming soon