# 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

![](/files/-MKzGHSgDLFOusS7Hq7F)

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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ir0nstone.gitbook.io/notes/binexp/heap/unlink-exploit.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
