Largebin Attack
Review Notes on Largebin Attack
Largebin Attack
Introduction
- Requires some method to modify chunk
bk_nextsize
. - Allows writing the large bin head chunk address to an arbitrary address.
Code Review
- The large bins range from size
0x400
onwards. - The large bin is maintained in sorted order.
- Has 2 separate pointers for forward and backward
fd_nextsize
andbk_nextsize
. - Large bin chunks have 2 pointers
fd_nextsize
andbk_nextsize
which points to the next and previous chunks respectively. - Large bin chunks can split into smaller chunks as required.
victim_index
is initialised to the largebin index for the requested size.- Sets
bck
pointer to linked-list at thevictim_index
. - Set
fwd
to forward ofbck
, i.e first chunk in the bin1 2 3
victim_index = largebin_index(size); bck = bin_at(av, victim_index); fwd = bck->fd;
- Check for whether
fwd
is equal tobck
, i.e, there are no more chunks in the list except the sentinel chunk. - If yes, the chunk is inserted directly into the largebin.
size
is or’d withPREV_INUSE
to speed up comparisons by avoiding the bit-masking step.bck->bk
points to the last chunk in the bin, the assertion checks whether it belongs to the main arena to catch race conditions where a chunk from a different thread arena gets linked into the bin.1 2 3 4 5 6 7
/* Maintain large bins in sorted order */ if (fwd != bck) { /* Or with inuse bit to speed comparisons */ size |= PREV_INUSE; /* If smaller than smallest, bypass loop below */ assert(chunk_main_arena(bck->bk));
- If the
size
of the chunk is less than that of the smallest chunk in the bin (since the largebins are sorted in descending order) the following part is executed. fwd
is set to the previous chunk, i.e, the bin header.bck
is set to the smallest chunk in the bin.victim->fd_nextsize
is set tofwd->fd
, i.e, the first chunk in the bin.victim->bk_nextsize
is set tofwd->fd->bk_nextsize
, i.e, the smallest chunk in the bin.fwd->fd->bk_nextsize
is set tovictim->bk_nextsize->fd_nextsize
which is set tovictim
, i.e, thevictim
chunk is linked to the smallest bin chunk’sfd_nextsize
and the first bin chunk’sbk_nextsize
.1 2 3 4 5 6 7
if ((unsigned long)(size) < (unsigned long)chunksize_nomask(bck->bk)) { fwd = bck; bck = bck->bk; victim->fd_nextsize = fwd->fd; victim->bk_nextsize = fwd->fd->bk_nextsize; fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
- If the chunk is not the smallest chunk to be added to the list then the following is executed.
- Loops through the nextsize list to find the current position of the chunk in the list while checking whether each chunk is part of the main arena.
1
2
3
4
5
6
} else {
assert(chunk_main_arena(fwd));
while ((unsigned long)size < chunksize_nomask(fwd)) {
fwd = fwd->fd_nextsize;
assert(chunk_main_arena(fwd));
}
- If the an existing chunk is found in the bin with the same size, then the chunk will be inserted in the second position of the sub-list of that size.
1 2 3
if ((unsigned long)size == (unsigned long)chunksize_nomask(fwd)) { /* Always insert in the second position. */ fwd = fwd->fd;
- Otherwise the chunk is inserted normally.
victim->fd_nextsize
is set to the next size chunk at that position.victim->bk_nextsize
is set to the previous size chunk at that position.- A check for whether the
bk_nextsize
andfd_nextsize
of thefwd
chunk is corrupted takes place. - If the check passes, the
victim
chunk is linked into the list. fwd->bk_nextsize
is set to thevictim
.victim->bk_nextsize->fd_nextsize
is set tovictim
, linking the previous and forward chunks.
1
2
3
4
5
6
7
8
} else {
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely(fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr("malloc(): largebin double linked list corrupted (nextsize)");
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
- If the chunk is to be directly added to the list as the bin is empty, then the following part executes.
bck
is initialised tofwd->bk
, i.e, the chunk before the end chunk.- A check for whether the first chunk in the list has been manipulated is done.
1 2 3 4
bck = fwd->bk; if (bck->fd != fwd) malloc_printerr("malloc(): largebin double linked list corrupted (bk)"); }
- If the bin is empty, then the
victim
is added to the list by linking it to itself in the nextsize list.1 2 3
} else { victim->fd_nextsize = victim->bk_nextsize = victim; }
- Finally,
mark_bin
marks the bin at that index as non-empty, this is done so that empty bins can be quickly identified using a binmap implementation. victim
is linked into the actual largebin list.1 2 3 4 5
mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
Exploitation
- Allocate a chunk that is of large bin size (p1) and another smaller chunk to prevent top chunk consolidation.
- Allocate another chunk that is of smaller size than the previous chunk but of the same large bin size, also add another chunk to prevent top chunk consolidation.
- Free the first chunk p1 and allocate another chunk larger than p1 such that p1 goes into the large bin.
- Free the second chunk p2 such that now we have one chunk in the large bin and one in the unsorted bin.
- Modify the bk_nextsize of chunk p1 to your target address -
0x20
. - Allocate another chunk larger than p2 so that p2 goes into the large bin.
- This effectively bypasses the
bk_nextsize
check via taking the “smaller than smallest” loop path as the check doesn’t take place here. - This writes the address of the chunk p2 to the target address.
This post is licensed under CC BY 4.0 by the author.