Find good fake chunk values in pwndbg:
got/plt (global offset table/procedure linkage table)
fini_array (called on program exit, protected by pointerguard)
heap (if there are function pointers)
tls_dtor_list (similar to fini array, protected by pointerguard)
malloc_hook & free_hook
If we can overflow a single byte we can change the size of the following chunk. If this chunk is then freed, this larger size is considered. If we now allocate this enlarged chunk again, we can fully overflow into following chunk! Usually you would do the following setup to overflow into chunk B:
add(0x38, b'A'*(0x38)+p64(0x70)) # idx 0
add(0x68, b'D'*0x40+p64(free_hook)) # idx 1
Overwrite size field of top chunk (e.g. via full heap overflow). Top chunk can extend down on heap, over libraries, into stack. We can also make it so large so it wraps around and points back into the heap. The following snippet writes a large value into the top chunk size field and then requests just enough space to get us to malloc hook so we can set it to an arbitrary value:
malloc(24, b"A"*24 +p64(0xfffffffffffffff1))
malloc((libc.sym.__malloc_hook - 0x20) - (heap + 0x20), "A")
Fastbins are singly linked lists (LIFO). The heap arena can only store 1 address per size, so the pointer to the next elements are stored in the heap metadata next to the user data.
Some Libc Versions have a check for double free, which checks that the top entry in the bin is not identical to the one that is being added. This can be bypassed by making sure "a" is not on top when freeing "a" again:
A full exploit could look like this:
chunk_A = malloc(0x68, "A"*0x68)
chunk_B = malloc(0x68, "B"*0x68)
dup = malloc(0x68, p64(libc.sym.__malloc_hook - 35))
malloc(0x68, b"A"*19+p64(libc.address + one_gadget))
We get a chunk twice into the fastbin list, overwriting the linked list pointer on the next malloc. We then "use up" the contents of the fastbin until we get the allocation at malloc hook.
When looking for valuable fake chunk targets we can also put one into the main arena directly (first write size field, then allocate fake chunk).