> For the complete documentation index, see [llms.txt](https://7-stars-servers.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://7-stars-servers.gitbook.io/docs/linux-internals/binary-exploitation/safe-unlinking.md).

# Safe Unlinking

### Background

Unlink Exploitation is a very powerful heap exploitation primitive that abused the unlink operation on doubly linked lists back in the day when Safe-Unlinking wasn't introduced in glibc.

In old versions of `ptmalloc` (glibc’s malloc), **free chunks** were stored in **doubly linked lists** (bins) something like this:

```bash
p <-> fd <-> fd->fd
```

Each free chunk `p` had 'p -> fd' forward pointer and 'p -> bk' backward pointer to point on the next and previous free chunk of data in the **doubly linked lists.**

<figure><img src="https://1403196296-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7iDlIelmS5trhjoQBiFK%2Fuploads%2F33htOY6o7cIL7DnsSSMA%2Fimage.png?alt=media&amp;token=ea5a129e-7ff9-4a1d-a89c-88076c76836e" alt=""><figcaption></figcaption></figure>

When removing (`unlink`ing) a chunk `p` from a bin, the allocator used logic equivalent to:

```
p->fd->bk = p->bk;
p->bk->fd = p->fd; 
```

What this means is that the chunk that is previous to p will have its fd point to the address of the chunk next to p and vice versa.

This is dangerous !

If an attacker could overflow into the freed chunk metadata and control the values of fd and bk pointers they could then turn this overflow into arbitrary write potentially overflowing valid pointers and causing the program to execute malicious code stored elsewhere in the binary.

### The Classic Unlink Exploit (pre-2005)

#### Requirements:

First, the attacker needs an overflow capability like:

* `strcpy`, `gets`, `sprintf`
* off-by-one
* integer overflow → undersized allocation

Second, A free chunk is adjacent to attacker data:

“Adjacent” does **not** mean logically related in code.\
It means **contiguous in virtual memory** inside the heap arena.

Example heap layout (addresses increase downward):

```css
0x0804a000  [ Chunk A | allocated | attacker-controlled data ]
0x0804a080  [ Chunk B | FREE | fd | bk | ... ]
```

Chunk A and Chunk B are **neighbors in memory**.

There is **no guard page**, **no padding**, **no red zone** between them.

Heap overflows do not magically jump around memory.

They overwrite **sequential bytes**.

So if:

* Chunk A is attacker-controlled
* Chunk B is immediately after it
* Chunk B is **free**

Then overflowing A will overwrite **B’s metadata**:

```
Chunk B metadata:
+--------------------+
| prev_size          |
| size               |
| fd   <-- TARGET    |
| bk   <-- VALUE     |
+--------------------+
```

This is exactly what unsafe unlink needs.

But why the victim chunk must be free ?&#x20;

That's simply because allocated chunks don't use fd and bk pointers. Only **free chunks** are inserted into bins and later unlinked use them.

So unless we overwrite these pointers for a free chunk, the overflow here become obsolete.

The attacker must arrange:

1. A chunk they can overflow
2. Followed immediately by
3. A chunk that is **freed before exploitation**

Otherwise:

* `fd` / `bk` are ignored
* `unlink()` is never called
* Exploit fails

#### Exploitation Logic:

So now The attacker chooses:

```c
p->fd = TARGET - offsetof(bk);
p->bk = VALUE;
```

Then unlink executes:

```c
*(TARGET) = VALUE;
```

That is:

* **where** = `TARGET`
* **what** = `VALUE`

The second write is often ignored or neutralized.

This is the core **unlink exploitation primitive**.

We can basically chose any place to write into and to put in it any value we want.\
A concrete example is overwriting the GOT entries.

If we leverage this to overwrite the address of the exit entry in the GOT table to another address that has a malicious code, this will make the program jump to that malicious code whenever the function exit is executed.

```perl
exit@GOT → shellcode
```

So we will need to setup the overflow to do this:

```c
p->fd = &exit@GOT - offsetof(bk);
p->bk = shellcode_address;
```

During the unlinking process it will become something like this:

```perl
exit@GOT = shellcode_address;
```

When `exit()` is later called → shellcode executes.

### Mitigation:

glibc version 2.3.6 from 2005 embedded a fix to this known exploit primitive called “Safe-Unlinking.”. Which is implemented as follows:

```c
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
    malloc_printerr ("corrupted double-linked list");
```

Before unlinking `p`, glibc verifies:

1. `p->fd->bk == p`
2. `p->bk->fd == p`

Graphically:

```
p <-> fd
^     |
|_____|
```

and

```
bk <-> p
|_____|
```

If **either relationship is broken**, the heap is considered corrupted and the program aborts.

While this exploit primitive was blocked more than 15 years ago, at the time no one came up with a similar solution to protect the pointers of the single-linked lists. Leveraging this weak spot, attackers shifted their focus to these unprotected single-linked lists such as Fast-Bins and also TCache (Thread Cache).
