Unlink Exploit
This particular attack was once quite common. However, two security checks were added in the unlink
MACRO ("corrupted size vs. prev_size" and "corrupted double-linked list") which reduced the impact of the attack to some extent. Nevertheless, it is worthwhile to spend some time on it. It exploits the pointer manipulation done in the unlink
MACRO while removing a chunk from a bin.
Consider this sample code (download the complete version here):
This might look a little complicated compared to other attacks. First, we malloc two chunks chunk1
and chunk2
with size 0x80
to ensure that they fall in the smallbin range. Next, we assume that the attacker somehow has unbounded control over the contents of chunk1
(this can be using any 'unsafe' function such as strcpy
on user input). Notice that both the chunks will lie in the memory side by side. The code shown above uses custom struct chunk_structure
for clarity purposes only. In an attack scenario, the attacker shall simply send bytes to fill in chunk1
that would have the same effect as above.
A new fake chunk is created in the 'data' part of chunk1
. The fd
and bk
pointers are adjusted to pass the "corrupted double-linked list" security check. The contents of the attacker are overflowed into chunk2
's header that sets appropriate prev_size
and prev_in_use
bit. This ensures that whenever chunk2
is freed, the fake_chunk
will be detected as 'freed' and will be unlinked
'. The following diagrams shows the current state of the various memory regions:
Carefully, try to understand how P->fd->bk == P
and P->bk->fd == P
checks are passed. This shall give an intuition regarding how to adjust the fd
and bk
pointers of the fake chunk.
As soon as chunk2
is freed, it is handled as a small bin. Recall that previous and next chunks (by memory) are checked whether they are 'free' or not. If any chunk is detected as 'free', it is unlinked
for the purpose of merging consecutive free chunks. The unlink
MACRO executes the following two instructions that modify pointers:
Set
P->fd->bk
=P->bk
.Set
P->bk->fd
=P->fd
.
In this case, both P->fd->bk
and P->bk->fd
point to the same location so only the second update is noticed. The following diagram shows the effects of the second update just after chunk2
is freed.
Now, we have chunk1
pointing to 3 addresses (16-bit) behind itself (&chunk1 - 3
). Hence, chunk1[3]
is in fact the chunk1
. Changing chunk1[3]
is like changing chunk1
. Notice that an attacker has a greater chance of getting an opportunity to update data at location chunk1
(chunk1[3] here
) instead of chunk1
itself. This completes the attack. In this example, chunk1
was made to point to a 'data' variable and changes through chunk1
were reflected on that variable.
Earlier, with the absence of security checks in unlink
, the two write instructions in the unlink
MACRO were used to achieve arbitrary writes. By overwriting .got
sections, this led to arbitrary code execution.
Last updated