This technique was developed by Alexander Peslyak(the father of Return Oriented Programming) in order to pwn the Netscape browser via a heap overflow.

Assumptions: Tcache is absent, heap overflow exists.

When a chunk adjacent to the top chunk that is too large to qualify for fastbins is freed (assuming there’s no tcache), it gets consolidated with the top chunk and the size of top chunk increases. But, when a chunk that is too large for fastbins and is not adjacent to the top chunk is freed, it gets inserted into the unsorted bin, the PREV_INUSE bit of the next chunk is cleared and PREV_SIZE field (the last QWORD of the current chunk) of the current chunk is set. This doesn’t happen in case of fastbins. When a non free chunk, adjacent to an unsorted chunk is freed, it gets consolidated with the free unsorted chunk and size of the free unsorted chunk increases. Thus, adjacent free unsorted chunks get consolidated. When a chunk is freed, a check is made whether its adjacent chunk is available for consolidation or not. malloc checks the PREV_INUSE of the chunk being freed to see if the previous chunk is free and also checks the PREV_INUSE flag of the next to next chunk to check whether the succeeding chunk is free. SIZE field is used to reach the next to next chunk. Once malloc finds out that either or both of these two chunks are consolidation candidates, it removes them from the free lists they’re linked into otherwise the chunk might get linked into two different lists or the same list twice. This results in forward or backward consolidation. After that, malloc adds the total space occupied by the two chunks and creates a larger chunk, sets the SIZE and PREV_SIZE fields. The interesting part is unlinking of a chunk from a free list. In older versions of libc, the unlinking process was done by the infamous unlink macro which gave a powerful arbitrary write primitive.

#define unlink(P, BK, FD)                                                     
{                                                                             
  BK = P->bk;                                                                 
  FD = P->fd;                                                                 
  FD->bk = BK;                                                                
  BK->fd = FD;                                                                
}                                                                             

Later on, some security checks were added to this unlink macro. Controlling the fd and bk of the chunk to be unlinked provides a powerful arbitrary write primitive.

Plan of attack: Overwrite the fd and bk pointers of the chunk being unlinked and then free a non fast chunk to activate the unlink macro which causes a reflected arbitrary write.