# Unlink Exploit

## Overview

When a chunk is removed from a bin, `unlink()` is called on the chunk. The unlink macro looks like this:

```c
FD = P->fd;    /* forward chunk */
BK = P->bk;    /* backward chunk */

FD->bk = BK;    /* update forward chunk's bk pointer */
BK->fd = FD;    /* updated backward chunk's fd pointer */
```

Note how `fd` and `bk` are written to location depending on `fd` and `bk`- if we control both `fd` and `bk`, we can get an _arbitrary write_.

Consider the following example:

We want to write the value `0x1000000c` to `0x5655578c`. If we had the ability to create a **fake free chunk**, we could choose the values for `fd` and `bk`. In this example, we would set `fd` to `0x56555780` (bear in mind the first `0x8` bytes in 32-bit would be for the metadata, so `P->fd` is actually 8 bytes off `P` and `P->bk` is 12 bytes off) and `bk` to `0x10000000`. Then when we `unlink()` this fake chunk, the process is as follows:

```c
FD = P->fd         (= 0x56555780)
BK = P->bk         (= 0x10000000)

FD->bk = BK        (0x56555780 + 0xc = 0x10000000)
BK->fd = FD        (0x10000000 + 0x8 = 0x56555780)
```

This may seem like a _lot_ to take in. It's a lot of seemingly random numbers. What you need to understand is `P->fd` just means _8 bytes off `P`_ and `P->bk` just means _12 bytes off `P`_.

If you imagine the chunk looking like

![](https://349224153-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MEwBGnjPgf263kl5vWP%2F-MKzBKNM-xiKdH5znOSm%2F-MKzGHSgDLFOusS7Hq7F%2Fimage.png?alt=media\&token=9815e616-149e-4734-a0cb-910587f8111b)

Then the `fd` and `bk` pointers point at the _start of the chunk_ - `prev_size`. So when overwriting the `fd` pointer _here:_

```c
FD->bk = BK        (0x56555780 + 0xc = 0x10000000)
```

`FD` points to `0x56555780`, and then `0xc` gets added on for `bk`, making the write actually occur at `0x5655578c`, which is what we wanted. _That_ is why we fake `fd` and `bk` values lower than the actual intended write location.

{% hint style="info" %}
In 64-bit, all the chunk data takes up `0x8` bytes each, so the offsets for `fd` and `bk` will be `0x10` and `0x18` respectively.
{% endhint %}

The slight issue with the unlink exploit is not only does `fd` get written to where you want, `bk` gets written as well - and if the location you are writing either of these to is protected memory, the binary will crash.

## Protections

More modern libc versions have a different version of the unlink macro, which looks like this:

```c
FD = P->fd;
BK = P->bk;

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
    malloc_printerr (check_action, "corrupted double-linked list", P, AV);
else {
    FD->bk = BK;
    BK->fd = FD;
}
```

Here `unlink()` check the `bk` pointer of the forward chunk and the `fd` pointer of the backward chunk and makes sure they point to `P`, which is unlikely if you fake a chunk. This quite significantly restricts where we can write using unlink.
